diff -urN gdal-2.3.2/GDALmake.opt.in gdal-2.3.2-pdf/GDALmake.opt.in
--- gdal-2.3.2/GDALmake.opt.in	2018-09-21 18:01:50.000000000 +0900
+++ gdal-2.3.2-pdf/GDALmake.opt.in	2019-12-12 13:24:28.223648374 +0900
@@ -468,6 +468,8 @@
 #
 
 HAVE_POPPLER = @HAVE_POPPLER@
+POPPLER_MAJOR_VERSION = @POPPLER_MAJOR_VERSION@
+POPPLER_MINOR_VERSION = @POPPLER_MINOR_VERSION@
 POPPLER_HAS_OPTCONTENT = @POPPLER_HAS_OPTCONTENT@
 POPPLER_BASE_STREAM_HAS_TWO_ARGS = @POPPLER_BASE_STREAM_HAS_TWO_ARGS@
 POPPLER_0_20_OR_LATER = @POPPLER_0_20_OR_LATER@
diff -urN gdal-2.3.2/configure gdal-2.3.2-pdf/configure
--- gdal-2.3.2/configure	2019-12-12 12:40:51.807185686 +0900
+++ gdal-2.3.2-pdf/configure	2019-12-12 13:13:35.928570492 +0900
@@ -663,6 +663,8 @@
 PODOFO_INC
 HAVE_PODOFO
 POPPLER_PLUGIN_LIB
+POPPLER_MINOR_VERSION
+POPPLER_MAJOR_VERSION
 POPPLER_INC
 POPPLER_0_58_OR_LATER
 POPPLER_0_23_OR_LATER
@@ -34381,6 +34383,8 @@
 
 
 HAVE_POPPLER=no
+POPPLER_MAJOR_VERSION=
+POPPLER_MINOR_VERSION=
 POPPLER_HAS_OPTCONTENT=no
 POPPLER_BASE_STREAM_HAS_TWO_ARGS=no
 POPPLER_0_20_OR_LATER=no
@@ -34538,8 +34542,21 @@
 $as_echo "disabled" >&6; }
 fi
 
+if test "$HAVE_POPPLER" = "yes"; then
+    POPPLER_VERSION=`$PKG_CONFIG --modversion poppler`
+    if test "$POPPLER_VERSION" != ""; then
+        HAVE_POPPLER=yes
+        POPPLER_MAJOR_VERSION=`expr $POPPLER_VERSION : '\([0-9]*\)'`
+        POPPLER_MINOR_VERSION=`expr $POPPLER_VERSION : '[0-9]*\.\([0-9]*\)'`
+    fi
+fi
+
 HAVE_POPPLER=$HAVE_POPPLER
 
+POPPLER_MAJOR_VERSION=$POPPLER_MAJOR_VERSION
+
+POPPLER_MINOR_VERSION=$POPPLER_MINOR_VERSION
+
 POPPLER_HAS_OPTCONTENT=$POPPLER_HAS_OPTCONTENT
 
 POPPLER_BASE_STREAM_HAS_TWO_ARGS=$POPPLER_BASE_STREAM_HAS_TWO_ARGS
@@ -34697,8 +34714,8 @@
 
     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 '#include <public/fpdfview.h>' > testpdfium.cpp
+        echo '#include <core/fpdfapi/page/cpdf_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/configure.ac gdal-2.3.2-pdf/configure.ac
--- gdal-2.3.2/configure.ac	2018-09-21 18:01:50.000000000 +0900
+++ gdal-2.3.2-pdf/configure.ac	2019-12-12 13:16:37.478044881 +0900
@@ -4441,6 +4441,8 @@
 AC_ARG_WITH(poppler,[  --with-poppler[=ARG]    Include poppler(for PDF) support (ARG=no(default), yes or poppler install path)],,)
 
 HAVE_POPPLER=no
+POPPLER_MAJOR_VERSION=
+POPPLER_MINOR_VERSION=
 POPPLER_HAS_OPTCONTENT=no
 POPPLER_BASE_STREAM_HAS_TWO_ARGS=no
 POPPLER_0_20_OR_LATER=no
@@ -4579,7 +4581,19 @@
     AC_MSG_RESULT([disabled])
 fi
 
+if test "$HAVE_POPPLER" = "yes"; then
+    PKG_PROG_PKG_CONFIG([0.21])
+    PKG_CHECK_MODULES([POPPLER], [poppler >= 0.23.0],
+            [POPPLER_VERSION=`$PKG_CONFIG --modversion poppler`], [POPPLER_VERSION=])
+    if test "$POPPLER_VERSION" != ""; then
+        POPPLER_MAJOR_VERSION=`expr $POPPLER_VERSION : '\([[0-9]]*\)'`
+        POPPLER_MINOR_VERSION=`expr $POPPLER_VERSION : '[[0-9]]*\.\([[0-9]]*\)'`
+    fi
+fi
+
 AC_SUBST(HAVE_POPPLER, $HAVE_POPPLER)
+AC_SUBST(POPPLER_MAJOR_VERSION, $POPPLER_MAJOR_VERSION)
+AC_SUBST(POPPLER_MINOR_VERSION, $POPPLER_MINOR_VERSION)
 AC_SUBST(POPPLER_HAS_OPTCONTENT, $POPPLER_HAS_OPTCONTENT)
 AC_SUBST(POPPLER_BASE_STREAM_HAS_TWO_ARGS, $POPPLER_BASE_STREAM_HAS_TWO_ARGS)
 AC_SUBST(POPPLER_0_20_OR_LATER, $POPPLER_0_20_OR_LATER)
@@ -4710,8 +4724,8 @@
 
     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 '#include <public/fpdfview.h>' > testpdfium.cpp
+        echo '#include <core/fpdfapi/page/cpdf_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/frmts/pdf/GNUmakefile gdal-2.3.2-pdf/frmts/pdf/GNUmakefile
--- gdal-2.3.2/frmts/pdf/GNUmakefile	2018-09-21 18:01:49.000000000 +0900
+++ gdal-2.3.2-pdf/frmts/pdf/GNUmakefile	2019-12-12 16:59:44.831994922 +0900
@@ -6,33 +6,12 @@
 
 ifeq ($(MACOSX_FRAMEWORK),yes)
 PLUGIN_DL = gdal_PDF.dylib
-LDFLAGS += -Wl,-undefined -Wl,dynamic_lookup -stdlib=libstdc++
-CPPFLAGS += -stdlib=libstdc++
+LDFLAGS += -Wl,-undefined -Wl,dynamic_lookup
 LD_SHARED = $(LD) -bundle
 endif
 
 ifeq ($(HAVE_POPPLER),yes)
-CPPFLAGS +=  -DHAVE_POPPLER
-endif
-
-ifeq ($(POPPLER_HAS_OPTCONTENT),yes)
-CPPFLAGS +=  -DPOPPLER_HAS_OPTCONTENT
-endif
-
-ifeq ($(POPPLER_BASE_STREAM_HAS_TWO_ARGS),yes)
-CPPFLAGS +=  -DPOPPLER_BASE_STREAM_HAS_TWO_ARGS
-endif
-
-ifeq ($(POPPLER_0_20_OR_LATER),yes)
-CPPFLAGS +=  -DPOPPLER_0_20_OR_LATER
-endif
-
-ifeq ($(POPPLER_0_23_OR_LATER),yes)
-CPPFLAGS +=  -DPOPPLER_0_23_OR_LATER
-endif
-
-ifeq ($(POPPLER_0_58_OR_LATER),yes)
-CPPFLAGS +=  -DPOPPLER_0_58_OR_LATER
+CPPFLAGS +=  -DHAVE_POPPLER -DPOPPLER_MAJOR_VERSION=$(POPPLER_MAJOR_VERSION) -DPOPPLER_MINOR_VERSION=$(POPPLER_MINOR_VERSION)
 endif
 
 ifeq ($(HAVE_PODOFO),yes)
@@ -64,4 +43,4 @@
 plugin: $(PLUGIN_DL)
 
 $(PLUGIN_DL):  $(OBJ)
-	$(LD_SHARED) $(OBJ) ../../ogr/ogrsf_frmts/o/ogrmemdatasource.o ../../ogr/ogrsf_frmts/o/ogrmemdriver.o ../../ogr/ogrsf_frmts/o/ogrmemlayer.o $(LDFLAGS) $(CONFIG_LIBS) -o $(PLUGIN_DL) $(PDFIUM_PLUGIN_LIB) $(POPPLER_PLUGIN_LIB) $(PODOFO_PLUGIN_LIB)
+	$(LD_SHARED) $(OBJ) ../../ogr/ogrsf_frmts/o/ogrmemdatasource.o ../../ogr/ogrsf_frmts/o/ogrmemdriver.o ../../ogr/ogrsf_frmts/o/ogrmemlayer.o $(LDFLAGS) -o $(PLUGIN_DL) $(PDFIUM_PLUGIN_LIB) $(POPPLER_PLUGIN_LIB) $(PODOFO_PLUGIN_LIB) $(CONFIG_LIBS)
diff -urN gdal-2.3.2/frmts/pdf/gdal_pdf.h gdal-2.3.2-pdf/frmts/pdf/gdal_pdf.h
--- gdal-2.3.2/frmts/pdf/gdal_pdf.h	2018-09-21 18:04:33.000000000 +0900
+++ gdal-2.3.2-pdf/frmts/pdf/gdal_pdf.h	2019-12-12 17:12:27.714841066 +0900
@@ -1,9 +1,9 @@
 /******************************************************************************
- * $Id: gdal_pdf.h 22f8ae3bf7bc3cccd970992655c63fc5254d3206 2018-04-08 20:13:05 +0200 Even Rouault $
+ * $Id$
  *
  * Project:  PDF Translator
  * Purpose:  Definition of classes for OGR .pdf driver.
- * Author:   Even Rouault, even dot rouault at mines dash paris dot org
+ * Author:   Even Rouault, even dot rouault at spatialys.com
  *
  ******************************************************************************
  *
@@ -13,7 +13,7 @@
  * Author: Martin Mikita <martin.mikita@klokantech.com>, xmikit00 @ FIT VUT Brno
  *
  ******************************************************************************
- * Copyright (c) 2010-2014, Even Rouault <even dot rouault at mines-paris dot org>
+ * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys.com>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -63,11 +63,15 @@
 #define     PDFLIB_PDFIUM     2
 #define     PDFLIB_COUNT      3
 
+#if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) || defined(HAVE_PDFIUM)
+#define HAVE_PDF_READ_SUPPORT
+#endif
+
 /************************************************************************/
 /*                             OGRPDFLayer                              */
 /************************************************************************/
 
-#if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) || defined(HAVE_PDFIUM)
+#ifdef HAVE_PDF_READ_SUPPORT
 
 class PDFDataset;
 
@@ -178,7 +182,7 @@
 #define MAX_TOKEN_SIZE 256
 #define TOKEN_STACK_SIZE 8
 
-#if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) || defined(HAVE_PDFIUM)
+#ifdef HAVE_PDF_READ_SUPPORT
 
 class PDFDataset final: public GDALPamDataset
 {
@@ -264,7 +268,7 @@
     void         ExploreLayersPoppler(GDALPDFArray* poArray, int nRecLevel, CPLString osTopLayer = "");
     void         FindLayersPoppler();
     void         TurnLayersOnOffPoppler();
-    std::map<CPLString, OptionalContentGroup*> oLayerOCGMapPoppler;
+    std::vector<std::pair<CPLString, OptionalContentGroup*> > oLayerOCGListPoppler;
 #endif
 
 #ifdef HAVE_PDFIUM
@@ -291,7 +295,19 @@
 
     CPLStringList osLayerList;
 
-    CPLStringList osLayerWithRefList;
+    struct LayerWithRef
+    {
+        CPLString        osName{};
+        GDALPDFObjectNum nOCGNum{};
+        int              nOCGGen = 0;
+
+        LayerWithRef(const CPLString& osNameIn,
+                     const GDALPDFObjectNum& nOCGNumIn,
+                     int nOCGGenIn) :
+            osName(osNameIn), nOCGNum(nOCGNumIn), nOCGGen(nOCGGenIn) {}
+    };
+    std::vector<LayerWithRef> aoLayerWithRef;
+
     CPLString     FindLayerOCG(GDALPDFDictionary* poPageDict,
                                const char* pszLayerName);
     void          FindLayersGeneric(GDALPDFDictionary* poPageDict);
@@ -362,6 +378,46 @@
     virtual CPLErr      SetProjection(const char* pszWKTIn) override;
     virtual CPLErr      SetGeoTransform(double* padfGeoTransform) override;
 
+#if 1
+    OGRSpatialReference* GetSpatialRef() {
+       const char* pWKT = GetProjectionRef();
+       if( !pWKT || pWKT[0] == '\0')
+       {
+           return nullptr;
+       }
+       OGRSpatialReference *m_pSRS = new OGRSpatialReference();
+       if( m_pSRS->importFromWkt(pWKT) != OGRERR_NONE )
+       {
+           return nullptr;
+       }
+        return m_pSRS;
+    }
+
+    CPLErr SetSpatialRef(const OGRSpatialReference* poSRS) {
+       if( !poSRS )
+       {
+           return SetProjection("");
+       }
+       char* pWKT = nullptr;
+       if( poSRS->exportToWkt(&pWKT) != OGRERR_NONE )
+       {
+           CPLFree(pWKT);
+           return CE_Failure;
+       }
+       auto ret = SetProjection(pWKT);
+       CPLFree(pWKT);
+       return ret;
+    }
+#else
+    // Since GDAL 3.0
+    const OGRSpatialReference* GetSpatialRef() const override {
+        return GetSpatialRefFromOldGetProjectionRef();
+    }
+    CPLErr SetSpatialRef(const OGRSpatialReference* poSRS) override {
+        return OldSetProjectionFromSetSpatialRef(poSRS);
+    }
+#endif
+
     virtual char      **GetMetadataDomainList() override;
     virtual char      **GetMetadata( const char * pszDomain = "" ) override;
     virtual CPLErr      SetMetadata( char ** papszMetadata,
@@ -381,9 +437,21 @@
 
     virtual int    GetGCPCount() override;
     virtual const char *GetGCPProjection() override;
+#if 0
+    const OGRSpatialReference* GetGCPSpatialRef() const override {
+        return GetGCPSpatialRefFromOldGetGCPProjection();
+    }
+#endif
     virtual const GDAL_GCP *GetGCPs() override;
     virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList,
                             const char *pszGCPProjection ) override;
+#if 0
+    using GDALPamDataset::SetGCPs;
+    CPLErr SetGCPs( int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
+                    const OGRSpatialReference* poSRS ) override {
+        return OldSetGCPsFromNew(nGCPCountIn, pasGCPListIn, poSRS);
+    }
+#endif
 
     CPLErr ReadPixels( int nReqXOff, int nReqYOff,
                        int nReqXSize, int nReqYSize,
@@ -399,7 +467,12 @@
 
     OGRGeometry        *GetGeometryFromMCID(int nMCID);
 
-    static GDALDataset *Open( GDALOpenInfo * );
+    GDALPDFObject*      GetPageObj() { return poPageObj; }
+    double              GetPageWidth() const { return dfPageWidth; }
+    double              GetPageHeight() const { return dfPageHeight; }
+
+    static PDFDataset  *Open( GDALOpenInfo * );
+    static GDALDataset *OpenWrapper( GDALOpenInfo * poOpenInfo ) { return Open(poOpenInfo); }
     static int          Identify( GDALOpenInfo * );
 
 #ifdef HAVE_PDFIUM
@@ -416,7 +489,7 @@
 /* ==================================================================== */
 /************************************************************************/
 
-class PDFRasterBand: public GDALPamRasterBand
+class PDFRasterBand : public GDALPamRasterBand
 {
     friend class PDFDataset;
 
@@ -445,7 +518,7 @@
 #endif
 };
 
-#endif /*  defined(HAVE_POPPLER) || defined(HAVE_PODOFO)|| defined(HAVE_PDFIUM) */
+#endif /* HAVE_PDF_READ_SUPPORT */
 
 /************************************************************************/
 /*                          PDFWritableDataset                          */
diff -urN gdal-2.3.2/frmts/pdf/makefile.vc gdal-2.3.2-pdf/frmts/pdf/makefile.vc
--- gdal-2.3.2/frmts/pdf/makefile.vc	2018-09-21 18:01:49.000000000 +0900
+++ gdal-2.3.2-pdf/frmts/pdf/makefile.vc	2019-12-12 12:44:04.921943906 +0900
@@ -1,63 +1,53 @@
-
-OBJ	=	pdfdataset.obj pdfio.obj pdfobject.obj pdfcreatecopy.obj ogrpdflayer.obj pdfwritabledataset.obj pdfreadvectors.obj
-
-PLUGIN_DLL =	gdal_PDF.dll
-
-GDAL_ROOT	=	..\..
-
-!INCLUDE $(GDAL_ROOT)\nmake.opt
-
-!IFDEF PDF_PLUGIN
-OBJ = $(OBJ) ..\..\ogr\ogrsf_frmts\mem\ogrmemdatasource.obj ..\..\ogr\ogrsf_frmts\mem\ogrmemdriver.obj ..\..\ogr\ogrsf_frmts\mem\ogrmemlayer.obj
-!ENDIF
-
-EXTRAFLAGS =  -I..\vrt -I..\mem -I..\..\ogr\ogrsf_frmts\mem $(POPPLER_EXTRAFLAGS) $(PODOFO_EXTRAFLAGS) $(PDFIUM_EXTRAFLAGS)
-
-!IFDEF POPPLER_ENABLED
-POPPLER_EXTRAFLAGS = $(POPPLER_CFLAGS) $(POPPLER_HAS_OPTCONTENT_FLAGS) $(POPPLER_BASE_STREAM_HAS_TWO_ARGS_FLAGS) $(POPPLER_0_20_OR_LATER_FLAGS) $(POPPLER_0_23_OR_LATER_FLAGS) $(POPPLER_0_58_OR_LATER_FLAGS) -DHAVE_POPPLER
-
-!IFDEF POPPLER_HAS_OPTCONTENT
-POPPLER_HAS_OPTCONTENT_FLAGS = -DPOPPLER_HAS_OPTCONTENT
-!ENDIF
-
-!IFDEF POPPLER_BASE_STREAM_HAS_TWO_ARGS
-POPPLER_BASE_STREAM_HAS_TWO_ARGS_FLAGS = -DPOPPLER_BASE_STREAM_HAS_TWO_ARGS
-!ENDIF
-
-!IFDEF POPPLER_0_20_OR_LATER
-POPPLER_0_20_OR_LATER_FLAGS = -DPOPPLER_0_20_OR_LATER
-!ENDIF
-
-!IFDEF POPPLER_0_23_OR_LATER
-POPPLER_0_23_OR_LATER_FLAGS = -DPOPPLER_0_23_OR_LATER
-!ENDIF
-
-!IFDEF POPPLER_0_58_OR_LATER
-POPPLER_0_58_OR_LATER_FLAGS = -DPOPPLER_0_58_OR_LATER
-!ENDIF
-
-!ENDIF
-
-!IFDEF PODOFO_ENABLED
-PODOFO_EXTRAFLAGS = $(PODOFO_CFLAGS) -DHAVE_PODOFO
-!ENDIF
-
-!IFDEF PDFIUM_ENABLED
-PDFIUM_EXTRAFLAGS = $(PDFIUM_CFLAGS) -DHAVE_PDFIUM -DNOMINMAX /wd4512
-!ENDIF
-
-default:	$(OBJ)
-	xcopy /D  /Y *.obj ..\o
-
-clean:
-	-del *.obj
-	
-plugin:	$(PLUGIN_DLL)
-
-$(PLUGIN_DLL): $(OBJ)
-	link /dll $(LDEBUG) /out:$(PLUGIN_DLL) $(OBJ) $(GDALLIB) $(POPPLER_LIBS) $(PODOFO_LIBS) $(PDFIUM_LIBS)
-	if exist $(PLUGIN_DLL).manifest mt -manifest $(PLUGIN_DLL).manifest -outputresource:$(PLUGIN_DLL);2
-
-plugin-install:
-	-mkdir $(PLUGINDIR)
-	$(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR)
+
+OBJ	=	pdfdataset.obj pdfio.obj pdfobject.obj pdfcreatecopy.obj ogrpdflayer.obj pdfwritabledataset.obj pdfreadvectors.obj pdfcreatefromcomposition.obj
+
+PLUGIN_DLL =	gdal_PDF.dll
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+!IFDEF PDF_PLUGIN
+OBJ = $(OBJ) ..\..\ogr\ogrsf_frmts\mem\ogrmemdatasource.obj ..\..\ogr\ogrsf_frmts\mem\ogrmemdriver.obj ..\..\ogr\ogrsf_frmts\mem\ogrmemlayer.obj
+!ENDIF
+
+EXTRAFLAGS =  -I..\vrt -I..\mem -I..\..\ogr\ogrsf_frmts\mem $(POPPLER_EXTRAFLAGS) $(PODOFO_EXTRAFLAGS) $(PDFIUM_EXTRAFLAGS)
+
+!IFDEF POPPLER_ENABLED
+POPPLER_EXTRAFLAGS = $(POPPLER_CFLAGS) $(POPPLER_VERSION_FLAGS) -DHAVE_POPPLER
+
+!IFDEF POPPLER_0_69_OR_LATER
+POPPLER_VERSION_FLAGS = -DPOPPLER_MAJOR_VERSION=0 -DPOPPLER_MINOR_VERSION=69
+!ELSEIFDEF POPPLER_0_58_OR_LATER
+POPPLER_VERSION_FLAGS = -DPOPPLER_MAJOR_VERSION=0 -DPOPPLER_MINOR_VERSION=58
+!ELSEIFDEF POPPLER_MAJOR_VERSION
+POPPLER_VERSION_FLAGS = -DPOPPLER_MAJOR_VERSION=$(POPPLER_MAJOR_VERSION) -DPOPPLER_MINOR_VERSION=$(POPPLER_MINOR_VERSION)
+!ELSE
+POPPLER_VERSION_FLAGS = -DPOPPLER_MAJOR_VERSION=0 -DPOPPLER_MINOR_VERSION=23
+!ENDIF
+
+!ENDIF
+
+!IFDEF PODOFO_ENABLED
+PODOFO_EXTRAFLAGS = $(PODOFO_CFLAGS) -DHAVE_PODOFO
+!ENDIF
+
+!IFDEF PDFIUM_ENABLED
+PDFIUM_EXTRAFLAGS = $(PDFIUM_CFLAGS) -DHAVE_PDFIUM -DNOMINMAX /wd4512
+!ENDIF
+
+default:	$(OBJ)
+	xcopy /D  /Y *.obj ..\o
+
+clean:
+	-del *.obj
+	
+plugin:	$(PLUGIN_DLL)
+
+$(PLUGIN_DLL): $(OBJ)
+	link /dll $(LDEBUG) /out:$(PLUGIN_DLL) $(OBJ) $(GDALLIB) $(POPPLER_LIBS) $(PODOFO_LIBS) $(PDFIUM_LIBS)
+	if exist $(PLUGIN_DLL).manifest mt -manifest $(PLUGIN_DLL).manifest -outputresource:$(PLUGIN_DLL);2
+
+plugin-install:
+	-mkdir $(PLUGINDIR)
+	$(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR)
diff -urN gdal-2.3.2/frmts/pdf/ogrpdflayer.cpp gdal-2.3.2-pdf/frmts/pdf/ogrpdflayer.cpp
--- gdal-2.3.2/frmts/pdf/ogrpdflayer.cpp	2018-09-21 18:04:33.000000000 +0900
+++ gdal-2.3.2-pdf/frmts/pdf/ogrpdflayer.cpp	2019-12-12 12:44:04.921943906 +0900
@@ -2,10 +2,10 @@
  *
  * Project:  PDF Translator
  * Purpose:  Implements OGRPDFDataSource class
- * Author:   Even Rouault, even dot rouault at mines dash paris dot org
+ * Author:   Even Rouault, even dot rouault at spatialys.com
  *
  ******************************************************************************
- * Copyright (c) 2012, Even Rouault <even dot rouault at mines-paris dot org>
+ * Copyright (c) 2012, Even Rouault <even dot rouault at spatialys.com>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -28,9 +28,9 @@
 
 #include "gdal_pdf.h"
 
-CPL_CVSID("$Id: ogrpdflayer.cpp 7e07230bbff24eb333608de4dbd460b7312839d0 2017-12-11 19:08:47Z Even Rouault $")
+CPL_CVSID("$Id$")
 
-#if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) || defined(HAVE_PDFIUM)
+#ifdef HAVE_PDF_READ_SUPPORT
 
 /************************************************************************/
 /*                            OGRPDFLayer()                             */
@@ -55,7 +55,8 @@
     for(int i=0;i<poArray->GetLength();i++)
     {
         GDALPDFObject* poFeatureObj = poArray->Get(i);
-        if (poFeatureObj->GetType() != PDFObjectType_Dictionary)
+        if (poFeatureObj == nullptr ||
+            poFeatureObj->GetType() != PDFObjectType_Dictionary)
             continue;
 
         GDALPDFObject* poA = poFeatureObj->GetDictionary()->Get("A");
@@ -76,7 +77,7 @@
         for(j = 0;j<poPArray->GetLength();j++)
         {
             GDALPDFObject* poKV = poPArray->Get(j);
-            if (poKV->GetType() == PDFObjectType_Dictionary)
+            if (poKV && poKV->GetType() == PDFObjectType_Dictionary)
             {
                 GDALPDFObject* poN = poKV->GetDictionary()->Get("N");
                 GDALPDFObject* poV = poKV->GetDictionary()->Get("V");
@@ -108,7 +109,7 @@
         for(j = 0;j<poPArray->GetLength();j++)
         {
             GDALPDFObject* poKV = poPArray->Get(j);
-            if (poKV->GetType() == PDFObjectType_Dictionary)
+            if (poKV && poKV->GetType() == PDFObjectType_Dictionary)
             {
                 GDALPDFObject* poN = poKV->GetDictionary()->Get("N");
                 GDALPDFObject* poV = poKV->GetDictionary()->Get("V");
@@ -168,7 +169,7 @@
         return OGRMemLayer::TestCapability(pszCap);
 }
 
-#endif /* defined(HAVE_POPPLER) || defined(HAVE_PODOFO) || defined(HAVE_PDFIUM) */
+#endif /* HAVE_PDF_READ_SUPPORT */
 
 /************************************************************************/
 /*                        OGRPDFWritableLayer()                         */
diff -urN gdal-2.3.2/frmts/pdf/pdfcreatecopy.cpp gdal-2.3.2-pdf/frmts/pdf/pdfcreatecopy.cpp
--- gdal-2.3.2/frmts/pdf/pdfcreatecopy.cpp	2018-09-21 18:04:33.000000000 +0900
+++ gdal-2.3.2-pdf/frmts/pdf/pdfcreatecopy.cpp	2019-12-12 16:17:53.651029796 +0900
@@ -2,10 +2,10 @@
  *
  * Project:  PDF driver
  * Purpose:  GDALDataset driver for PDF dataset.
- * Author:   Even Rouault, <even dot rouault at mines dash paris dot org>
+ * Author:   Even Rouault, <even dot rouault at spatialys.com>
  *
  ******************************************************************************
- * Copyright (c) 2012-2014, Even Rouault <even dot rouault at mines-paris dot org>
+ * Copyright (c) 2012-2019, Even Rouault <even dot rouault at spatialys.com>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -39,55 +39,102 @@
 #include "pdfobject.h"
 
 #include <cmath>
-
 #include <algorithm>
+#include <utility>
 #include <vector>
 
-/* Cf PDF reference v1.7, Appendix C, page 993 */
-#define MAXIMUM_SIZE_IN_UNITS   14400
+CPL_CVSID("$Id$")
 
-CPL_CVSID("$Id: pdfcreatecopy.cpp 971ad299681ca1ea2e1b800e88209f426b77e9aa 2018-04-17 12:14:43 +0200 Even Rouault $")
+/************************************************************************/
+/*                        GDALPDFBaseWriter()                           */
+/************************************************************************/
 
-#define PIXEL_TO_GEO_X(x,y) adfGeoTransform[0] + x * adfGeoTransform[1] + y * adfGeoTransform[2]
-#define PIXEL_TO_GEO_Y(x,y) adfGeoTransform[3] + x * adfGeoTransform[4] + y * adfGeoTransform[5]
+GDALPDFBaseWriter::GDALPDFBaseWriter(VSILFILE* fp):
+    m_fp(fp)
+{}
 
-class GDALFakePDFDataset : public GDALDataset
+
+/************************************************************************/
+/*                       ~GDALPDFBaseWriter()                           */
+/************************************************************************/
+
+GDALPDFBaseWriter::~GDALPDFBaseWriter()
 {
-    public:
-        GDALFakePDFDataset() {}
-};
+    Close();
+}
 
 /************************************************************************/
-/*                         GDALPDFWriter()                              */
+/*                              ~Close()                                */
 /************************************************************************/
 
-GDALPDFWriter::GDALPDFWriter( VSILFILE* fpIn, int bAppend ) :
-    fp(fpIn),
-    nInfoId(0),
-    nInfoGen(0),
-    nPageResourceId(0),
-    nStructTreeRootId(0),
-    nCatalogId(0),
-    nCatalogGen(0),
-    nXMPId(0),
-    nXMPGen(0),
-    nNamesId(0),
-    bInWriteObj(FALSE),
-    nLastStartXRef(0),
-    nLastXRefSize(0),
-    bCanUpdate(FALSE)
-{
-    if( !bAppend )
-    {
-        VSIFPrintfL(fp, "%%PDF-1.6\n");
-
-        // See PDF 1.7 reference, page 92. Write 4 non-ASCII bytes to indicate
-        // that the content will be binary.
-        VSIFPrintfL(fp, "%%%c%c%c%c\n", 0xFF, 0xFF, 0xFF, 0xFF);
+void GDALPDFBaseWriter::Close()
+{
+    if( m_fp )
+    {
+        VSIFCloseL(m_fp);
+        m_fp = nullptr;
+    }
+}
 
-        nPageResourceId = AllocNewObject();
-        nCatalogId = AllocNewObject();
+/************************************************************************/
+/*                           GDALPDFUpdateWriter()                      */
+/************************************************************************/
+
+GDALPDFUpdateWriter::GDALPDFUpdateWriter(VSILFILE* fp):
+    GDALPDFBaseWriter(fp)
+{}
+
+/************************************************************************/
+/*                          ~GDALPDFUpdateWriter()                      */
+/************************************************************************/
+
+GDALPDFUpdateWriter::~GDALPDFUpdateWriter()
+{
+    Close();
+}
+
+/************************************************************************/
+/*                              ~Close()                                */
+/************************************************************************/
+
+void GDALPDFUpdateWriter::Close()
+{
+    if (m_fp)
+    {
+        CPLAssert(!m_bInWriteObj);
+        if (m_bUpdateNeeded)
+        {
+            WriteXRefTableAndTrailer(true, m_nLastStartXRef);
+        }
     }
+    GDALPDFBaseWriter::Close();
+}
+
+
+/************************************************************************/
+/*                          StartNewDoc()                               */
+/************************************************************************/
+
+void GDALPDFBaseWriter::StartNewDoc()
+{
+    VSIFPrintfL(m_fp, "%%PDF-1.6\n");
+
+    // See PDF 1.7 reference, page 92. Write 4 non-ASCII bytes to indicate
+    // that the content will be binary.
+    VSIFPrintfL(m_fp, "%%%c%c%c%c\n", 0xFF, 0xFF, 0xFF, 0xFF);
+
+    m_nPageResourceId = AllocNewObject();
+    m_nCatalogId = AllocNewObject();
+}
+
+/************************************************************************/
+/*                         GDALPDFWriter()                              */
+/************************************************************************/
+
+GDALPDFWriter::GDALPDFWriter( VSILFILE* fpIn ) :
+    GDALPDFBaseWriter(fpIn)
+{
+    StartNewDoc();
 }
 
 /************************************************************************/
@@ -103,7 +150,7 @@
 /*                          ParseIndirectRef()                          */
 /************************************************************************/
 
-static int ParseIndirectRef(const char* pszStr, int& nNum, int &nGen)
+static int ParseIndirectRef(const char* pszStr, GDALPDFObjectNum& nNum, int &nGen)
 {
     while(*pszStr == ' ')
         pszStr ++;
@@ -133,11 +180,11 @@
 /*                       ParseTrailerAndXRef()                          */
 /************************************************************************/
 
-int GDALPDFWriter::ParseTrailerAndXRef()
+int GDALPDFUpdateWriter::ParseTrailerAndXRef()
 {
-    VSIFSeekL(fp, 0, SEEK_END);
+    VSIFSeekL(m_fp, 0, SEEK_END);
     char szBuf[1024+1];
-    vsi_l_offset nOffset = VSIFTellL(fp);
+    vsi_l_offset nOffset = VSIFTellL(m_fp);
 
     if (nOffset > 128)
         nOffset -= 128;
@@ -145,8 +192,8 @@
         nOffset = 0;
 
     /* Find startxref section */
-    VSIFSeekL(fp, nOffset, SEEK_SET);
-    int nRead = (int) VSIFReadL(szBuf, 1, 128, fp);
+    VSIFSeekL(m_fp, nOffset, SEEK_SET);
+    int nRead = (int) VSIFReadL(szBuf, 1, 128, m_fp);
     szBuf[nRead] = 0;
     if (nRead < 9)
         return FALSE;
@@ -175,14 +222,14 @@
         return FALSE;
     }
 
-    nLastStartXRef = CPLScanUIntBig(pszStartXRef,16);
+    m_nLastStartXRef = CPLScanUIntBig(pszStartXRef,16);
 
     /* Skip to beginning of xref section */
-    VSIFSeekL(fp, nLastStartXRef, SEEK_SET);
+    VSIFSeekL(m_fp, m_nLastStartXRef, SEEK_SET);
 
     /* And skip to trailer */
     const char* pszLine = nullptr;
-    while( (pszLine = CPLReadLineL(fp)) != nullptr)
+    while( (pszLine = CPLReadLineL(m_fp)) != nullptr)
     {
         if (STARTS_WITH(pszLine, "trailer"))
             break;
@@ -195,7 +242,7 @@
     }
 
     /* Read trailer content */
-    nRead = (int) VSIFReadL(szBuf, 1, 1024, fp);
+    nRead = (int) VSIFReadL(szBuf, 1, 1024, m_fp);
     szBuf[nRead] = 0;
 
     /* Find XRef size */
@@ -208,7 +255,7 @@
     pszSize += 5;
     while(*pszSize == ' ')
         pszSize ++;
-    nLastXRefSize = atoi(pszSize);
+    m_nLastXRefSize = atoi(pszSize);
 
     /* Find Root object */
     const char* pszRoot = strstr(szBuf, "/Root");
@@ -221,7 +268,7 @@
     while(*pszRoot == ' ')
         pszRoot ++;
 
-    if (!ParseIndirectRef(pszRoot, nCatalogId, nCatalogGen))
+    if (!ParseIndirectRef(pszRoot, m_nCatalogId, m_nCatalogGen))
     {
         CPLError(CE_Failure, CPLE_AppDefined, "Cannot parse trailer /Root");
         return FALSE;
@@ -235,15 +282,15 @@
         while(*pszInfo == ' ')
             pszInfo ++;
 
-        if (!ParseIndirectRef(pszInfo, nInfoId, nInfoGen))
+        if (!ParseIndirectRef(pszInfo, m_nInfoId, m_nInfoGen))
         {
             CPLError(CE_Failure, CPLE_AppDefined, "Cannot parse trailer /Info");
-            nInfoId = 0;
-            nInfoGen = 0;
+            m_nInfoId = 0;
+            m_nInfoGen = 0;
         }
     }
 
-    VSIFSeekL(fp, 0, SEEK_END);
+    VSIFSeekL(m_fp, 0, SEEK_END);
 
     return TRUE;
 }
@@ -254,43 +301,38 @@
 
 void GDALPDFWriter::Close()
 {
-    if (fp)
+    if (m_fp)
     {
-        CPLAssert(!bInWriteObj);
-        if (nPageResourceId)
+        CPLAssert(!m_bInWriteObj);
+        if (m_nPageResourceId.toBool())
         {
             WritePages();
-            WriteXRefTableAndTrailer();
-        }
-        else if (bCanUpdate)
-        {
-            WriteXRefTableAndTrailer();
+            WriteXRefTableAndTrailer(false, 0);
         }
-        VSIFCloseL(fp);
     }
-    fp = nullptr;
+    GDALPDFBaseWriter::Close();
 }
 
 /************************************************************************/
 /*                           UpdateProj()                               */
 /************************************************************************/
 
-void GDALPDFWriter::UpdateProj(GDALDataset* poSrcDS,
+void GDALPDFUpdateWriter::UpdateProj(GDALDataset* poSrcDS,
                                double dfDPI,
                                GDALPDFDictionaryRW* poPageDict,
-                               int nPageNum, int nPageGen)
+                               const GDALPDFObjectNum& nPageId, int nPageGen)
 {
-    bCanUpdate = TRUE;
-    if ((int)asXRefEntries.size() < nLastXRefSize - 1)
-        asXRefEntries.resize(nLastXRefSize - 1);
+    m_bUpdateNeeded = true;
+    if ((int)m_asXRefEntries.size() < m_nLastXRefSize - 1)
+        m_asXRefEntries.resize(m_nLastXRefSize - 1);
 
-    int nViewportId = 0;
-    int nLGIDictId = 0;
+    GDALPDFObjectNum nViewportId;
+    GDALPDFObjectNum nLGIDictId;
 
-    CPLAssert(nPageNum != 0);
+    CPLAssert(nPageId.toBool());
     CPLAssert(poPageDict != nullptr);
 
-    PDFMargins sMargins = {0, 0, 0, 0};
+    PDFMargins sMargins;
 
     const char* pszGEO_ENCODING = CPLGetConfigOption("GDAL_PDF_GEO_ENCODING", "ISO32000");
     if (EQUAL(pszGEO_ENCODING, "ISO32000") || EQUAL(pszGEO_ENCODING, "BOTH"))
@@ -309,8 +351,8 @@
         int nVPId = poVP->GetRefNum();
         if (nVPId)
         {
-            asXRefEntries[nVPId - 1].bFree = TRUE;
-            asXRefEntries[nVPId - 1].nGen ++;
+            m_asXRefEntries[nVPId - 1].bFree = TRUE;
+            m_asXRefEntries[nVPId - 1].nGen ++;
         }
     }
 #endif
@@ -318,19 +360,19 @@
     poPageDict->Remove("VP");
     poPageDict->Remove("LGIDict");
 
-    if (nViewportId)
+    if (nViewportId.toBool())
     {
         poPageDict->Add("VP", &((new GDALPDFArrayRW())->
                 Add(nViewportId, 0)));
     }
 
-    if (nLGIDictId)
+    if (nLGIDictId.toBool())
     {
         poPageDict->Add("LGIDict", nLGIDictId, 0);
     }
 
-    StartObj(nPageNum, nPageGen);
-    VSIFPrintfL(fp, "%s\n", poPageDict->Serialize().c_str());
+    StartObj(nPageId, nPageGen);
+    VSIFPrintfL(m_fp, "%s\n", poPageDict->Serialize().c_str());
     EndObj();
 }
 
@@ -338,22 +380,22 @@
 /*                           UpdateInfo()                               */
 /************************************************************************/
 
-void GDALPDFWriter::UpdateInfo(GDALDataset* poSrcDS)
+void GDALPDFUpdateWriter::UpdateInfo(GDALDataset* poSrcDS)
 {
-    bCanUpdate = TRUE;
-    if ((int)asXRefEntries.size() < nLastXRefSize - 1)
-        asXRefEntries.resize(nLastXRefSize - 1);
+    m_bUpdateNeeded = true;
+    if ((int)m_asXRefEntries.size() < m_nLastXRefSize - 1)
+        m_asXRefEntries.resize(m_nLastXRefSize - 1);
 
-    int nNewInfoId = SetInfo(poSrcDS, nullptr);
+    auto nNewInfoId = SetInfo(poSrcDS, nullptr);
     /* Write empty info, because podofo driver will find the dangling info instead */
-    if (nNewInfoId == 0 && nInfoId != 0)
+    if (!nNewInfoId.toBool() && m_nInfoId.toBool())
     {
 #ifdef invalidate_xref_entry
-        asXRefEntries[nInfoId - 1].bFree = TRUE;
-        asXRefEntries[nInfoId - 1].nGen ++;
+        m_asXRefEntries[m_nInfoId.toInt() - 1].bFree = TRUE;
+        m_asXRefEntries[m_nInfoId.toInt() - 1].nGen ++;
 #else
-        StartObj(nInfoId, nInfoGen);
-        VSIFPrintfL(fp, "<< >>\n");
+        StartObj(m_nInfoId, m_nInfoGen);
+        VSIFPrintfL(m_fp, "<< >>\n");
         EndObj();
 #endif
     }
@@ -363,39 +405,39 @@
 /*                           UpdateXMP()                                */
 /************************************************************************/
 
-void GDALPDFWriter::UpdateXMP(GDALDataset* poSrcDS,
+void GDALPDFUpdateWriter::UpdateXMP(GDALDataset* poSrcDS,
                               GDALPDFDictionaryRW* poCatalogDict)
 {
-    bCanUpdate = TRUE;
-    if ((int)asXRefEntries.size() < nLastXRefSize - 1)
-        asXRefEntries.resize(nLastXRefSize - 1);
+    m_bUpdateNeeded = true;
+    if ((int)m_asXRefEntries.size() < m_nLastXRefSize - 1)
+        m_asXRefEntries.resize(m_nLastXRefSize - 1);
 
-    CPLAssert(nCatalogId != 0);
+    CPLAssert(m_nCatalogId.toBool());
     CPLAssert(poCatalogDict != nullptr);
 
     GDALPDFObject* poMetadata = poCatalogDict->Get("Metadata");
     if (poMetadata)
     {
-        nXMPId = poMetadata->GetRefNum();
-        nXMPGen = poMetadata->GetRefGen();
+        m_nXMPId = poMetadata->GetRefNum();
+        m_nXMPGen = poMetadata->GetRefGen();
     }
 
     poCatalogDict->Remove("Metadata");
-    int nNewXMPId = SetXMP(poSrcDS, nullptr);
+    auto nNewXMPId = SetXMP(poSrcDS, nullptr);
 
     /* Write empty metadata, because podofo driver will find the dangling info instead */
-    if (nNewXMPId == 0 && nXMPId != 0)
+    if (!nNewXMPId.toBool() && m_nXMPId.toBool())
     {
-        StartObj(nXMPId, nXMPGen);
-        VSIFPrintfL(fp, "<< >>\n");
+        StartObj(m_nXMPId, m_nXMPGen);
+        VSIFPrintfL(m_fp, "<< >>\n");
         EndObj();
     }
 
-    if (nXMPId)
-        poCatalogDict->Add("Metadata", nXMPId, 0);
+    if (m_nXMPId.toBool())
+        poCatalogDict->Add("Metadata", m_nXMPId, 0);
 
-    StartObj(nCatalogId, nCatalogGen);
-    VSIFPrintfL(fp, "%s\n", poCatalogDict->Serialize().c_str());
+    StartObj(m_nCatalogId, m_nCatalogGen);
+    VSIFPrintfL(m_fp, "%s\n", poCatalogDict->Serialize().c_str());
     EndObj();
 }
 
@@ -403,46 +445,47 @@
 /*                           AllocNewObject()                           */
 /************************************************************************/
 
-int GDALPDFWriter::AllocNewObject()
+GDALPDFObjectNum GDALPDFBaseWriter::AllocNewObject()
 {
-    asXRefEntries.push_back(GDALXRefEntry());
-    return (int)asXRefEntries.size();
+    m_asXRefEntries.push_back(GDALXRefEntry());
+    return GDALPDFObjectNum(static_cast<int>(m_asXRefEntries.size()));
 }
 
 /************************************************************************/
 /*                        WriteXRefTableAndTrailer()                    */
 /************************************************************************/
 
-void GDALPDFWriter::WriteXRefTableAndTrailer()
+void GDALPDFBaseWriter::WriteXRefTableAndTrailer(bool bUpdate,
+                                                 vsi_l_offset nLastStartXRef)
 {
-    vsi_l_offset nOffsetXREF = VSIFTellL(fp);
-    VSIFPrintfL(fp, "xref\n");
+    vsi_l_offset nOffsetXREF = VSIFTellL(m_fp);
+    VSIFPrintfL(m_fp, "xref\n");
 
     char buffer[16];
-    if (bCanUpdate)
+    if (bUpdate)
     {
-        VSIFPrintfL(fp, "0 1\n");
-        VSIFPrintfL(fp, "0000000000 65535 f \n");
-        for(size_t i=0;i<asXRefEntries.size();)
+        VSIFPrintfL(m_fp, "0 1\n");
+        VSIFPrintfL(m_fp, "0000000000 65535 f \n");
+        for(size_t i=0;i<m_asXRefEntries.size();)
         {
-            if (asXRefEntries[i].nOffset != 0 || asXRefEntries[i].bFree)
+            if (m_asXRefEntries[i].nOffset != 0 || m_asXRefEntries[i].bFree)
             {
                 /* Find number of consecutive objects */
                 size_t nCount = 1;
-                while(i + nCount <asXRefEntries.size() &&
-                    (asXRefEntries[i + nCount].nOffset != 0 || asXRefEntries[i + nCount].bFree))
+                while(i + nCount <m_asXRefEntries.size() &&
+                    (m_asXRefEntries[i + nCount].nOffset != 0 || m_asXRefEntries[i + nCount].bFree))
                     nCount ++;
 
-                VSIFPrintfL(fp, "%d %d\n", (int)i + 1, (int)nCount);
+                VSIFPrintfL(m_fp, "%d %d\n", (int)i + 1, (int)nCount);
                 size_t iEnd = i + nCount;
                 for(; i < iEnd; i++)
                 {
                     snprintf (buffer, sizeof(buffer),
                               "%010" CPL_FRMT_GB_WITHOUT_PREFIX "u",
-                              asXRefEntries[i].nOffset);
-                    VSIFPrintfL(fp, "%s %05d %c \n",
-                                buffer, asXRefEntries[i].nGen,
-                                asXRefEntries[i].bFree ? 'f' : 'n');
+                              m_asXRefEntries[i].nOffset);
+                    VSIFPrintfL(m_fp, "%s %05d %c \n",
+                                buffer, m_asXRefEntries[i].nGen,
+                                m_asXRefEntries[i].bFree ? 'f' : 'n');
                 }
             }
             else
@@ -453,29 +496,29 @@
     }
     else
     {
-        VSIFPrintfL(fp, "%d %d\n",
-                    0, (int)asXRefEntries.size() + 1);
-        VSIFPrintfL(fp, "0000000000 65535 f \n");
-        for(size_t i=0;i<asXRefEntries.size();i++)
+        VSIFPrintfL(m_fp, "%d %d\n",
+                    0, (int)m_asXRefEntries.size() + 1);
+        VSIFPrintfL(m_fp, "0000000000 65535 f \n");
+        for(size_t i=0;i<m_asXRefEntries.size();i++)
         {
             snprintf (buffer, sizeof(buffer),
                       "%010" CPL_FRMT_GB_WITHOUT_PREFIX "u",
-                      asXRefEntries[i].nOffset);
-            VSIFPrintfL(fp, "%s %05d n \n", buffer, asXRefEntries[i].nGen);
+                      m_asXRefEntries[i].nOffset);
+            VSIFPrintfL(m_fp, "%s %05d n \n", buffer, m_asXRefEntries[i].nGen);
         }
     }
 
-    VSIFPrintfL(fp, "trailer\n");
+    VSIFPrintfL(m_fp, "trailer\n");
     GDALPDFDictionaryRW oDict;
-    oDict.Add("Size", (int)asXRefEntries.size() + 1)
-         .Add("Root", nCatalogId, nCatalogGen);
-    if (nInfoId)
-        oDict.Add("Info", nInfoId, nInfoGen);
+    oDict.Add("Size", (int)m_asXRefEntries.size() + 1)
+         .Add("Root", m_nCatalogId, m_nCatalogGen);
+    if (m_nInfoId.toBool())
+        oDict.Add("Info", m_nInfoId, m_nInfoGen);
     if (nLastStartXRef)
         oDict.Add("Prev", (double)nLastStartXRef);
-    VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
+    VSIFPrintfL(m_fp, "%s\n", oDict.Serialize().c_str());
 
-    VSIFPrintfL(fp,
+    VSIFPrintfL(m_fp,
                 "startxref\n"
                 CPL_FRMT_GUIB "\n"
                 "%%%%EOF\n",
@@ -486,26 +529,96 @@
 /*                              StartObj()                              */
 /************************************************************************/
 
-void GDALPDFWriter::StartObj(int nObjectId, int nGen)
+void GDALPDFBaseWriter::StartObj(const GDALPDFObjectNum& nObjectId, int nGen)
 {
-    CPLAssert(!bInWriteObj);
-    CPLAssert(nObjectId - 1 < (int)asXRefEntries.size());
-    CPLAssert(asXRefEntries[nObjectId - 1].nOffset == 0);
-    asXRefEntries[nObjectId - 1].nOffset = VSIFTellL(fp);
-    asXRefEntries[nObjectId - 1].nGen = nGen;
-    VSIFPrintfL(fp, "%d %d obj\n", nObjectId, nGen);
-    bInWriteObj = TRUE;
+    CPLAssert(!m_bInWriteObj);
+    CPLAssert(nObjectId.toInt() - 1 < (int)m_asXRefEntries.size());
+    CPLAssert(m_asXRefEntries[nObjectId.toInt() - 1].nOffset == 0);
+    m_asXRefEntries[nObjectId.toInt() - 1].nOffset = VSIFTellL(m_fp);
+    m_asXRefEntries[nObjectId.toInt() - 1].nGen = nGen;
+    VSIFPrintfL(m_fp, "%d %d obj\n", nObjectId.toInt(), nGen);
+    m_bInWriteObj = true;
 }
 
 /************************************************************************/
 /*                               EndObj()                               */
 /************************************************************************/
 
-void GDALPDFWriter::EndObj()
+void GDALPDFBaseWriter::EndObj()
+{
+    CPLAssert(m_bInWriteObj);
+    CPLAssert(!m_fpBack);
+    VSIFPrintfL(m_fp, "endobj\n");
+    m_bInWriteObj = false;
+}
+
+/************************************************************************/
+/*                         StartObjWithStream()                         */
+/************************************************************************/
+
+void GDALPDFBaseWriter::StartObjWithStream(const GDALPDFObjectNum& nObjectId,
+                                           GDALPDFDictionaryRW& oDict,
+                                           bool bDeflate)
+{
+    CPLAssert(!m_nContentLengthId.toBool());
+    CPLAssert(!m_fpGZip);
+    CPLAssert(!m_fpBack);
+    CPLAssert(m_nStreamStart == 0);
+
+    m_nContentLengthId = AllocNewObject();
+
+    StartObj(nObjectId);
+    {
+        oDict.Add("Length", m_nContentLengthId, 0);
+        if( bDeflate )
+        {
+            oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode"));
+        }
+        VSIFPrintfL(m_fp, "%s\n", oDict.Serialize().c_str());
+    }
+
+    /* -------------------------------------------------------------- */
+    /*  Write content stream                                          */
+    /* -------------------------------------------------------------- */
+    VSIFPrintfL(m_fp, "stream\n");
+    m_nStreamStart = VSIFTellL(m_fp);
+
+    m_fpGZip = nullptr;
+    m_fpBack = m_fp;
+    if( bDeflate )
+    {
+        m_fpGZip = reinterpret_cast<VSILFILE*>(VSICreateGZipWritable(
+            reinterpret_cast<VSIVirtualHandle*>(m_fp), TRUE, FALSE ));
+        m_fp = m_fpGZip;
+    }
+}
+
+/************************************************************************/
+/*                          EndObjWithStream()                          */
+/************************************************************************/
+
+void GDALPDFBaseWriter::EndObjWithStream()
 {
-    CPLAssert(bInWriteObj);
-    VSIFPrintfL(fp, "endobj\n");
-    bInWriteObj = FALSE;
+    if (m_fpGZip)
+        VSIFCloseL(m_fpGZip);
+    m_fp = m_fpBack;
+    m_fpBack = nullptr;
+
+    vsi_l_offset nStreamEnd = VSIFTellL(m_fp);
+    if (m_fpGZip)
+        VSIFPrintfL(m_fp, "\n");
+    m_fpGZip = nullptr;
+    VSIFPrintfL(m_fp, "endstream\n");
+    EndObj();
+
+    StartObj(m_nContentLengthId);
+    VSIFPrintfL(m_fp,
+                "   %ld\n",
+                static_cast<long>(nStreamEnd - m_nStreamStart));
+    EndObj();
+
+    m_nContentLengthId = GDALPDFObjectNum();
+    m_nStreamStart = 0;
 }
 
 /************************************************************************/
@@ -557,7 +670,7 @@
 /*                         WriteSRS_ISO32000()                          */
 /************************************************************************/
 
-int  GDALPDFWriter::WriteSRS_ISO32000(GDALDataset* poSrcDS,
+GDALPDFObjectNum GDALPDFBaseWriter::WriteSRS_ISO32000(GDALDataset* poSrcDS,
                                       double dfUserUnit,
                                       const char* pszNEATLINE,
                                       PDFMargins* psMargins,
@@ -574,10 +687,10 @@
         pszWKT = poSrcDS->GetGCPProjection();
 
     if( !bHasGT && pasGCPList == nullptr )
-        return 0;
+        return GDALPDFObjectNum();
 
     if( pszWKT == nullptr || EQUAL(pszWKT, "") )
-        return 0;
+        return GDALPDFObjectNum();
 
     double adfGPTS[8];
 
@@ -664,7 +777,7 @@
         {
             CPLError(CE_Failure, CPLE_NotSupported,
                      "GCPs should form a rectangle in pixel space");
-            return 0;
+            return GDALPDFObjectNum();
         }
 
         dfULPixel = pasGCPList[iUL].dfGCPPixel;
@@ -691,37 +804,39 @@
     else
     {
         /* Upper-left */
-        adfGPTS[0] = PIXEL_TO_GEO_X(0, 0);
-        adfGPTS[1] = PIXEL_TO_GEO_Y(0, 0);
+        adfGPTS[0] = APPLY_GT_X(adfGeoTransform, 0, 0);
+        adfGPTS[1] = APPLY_GT_Y(adfGeoTransform, 0, 0);
 
         /* Lower-left */
-        adfGPTS[2] = PIXEL_TO_GEO_X(0, nHeight);
-        adfGPTS[3] = PIXEL_TO_GEO_Y(0, nHeight);
+        adfGPTS[2] = APPLY_GT_X(adfGeoTransform, 0, nHeight);
+        adfGPTS[3] = APPLY_GT_Y(adfGeoTransform, 0, nHeight);
 
         /* Lower-right */
-        adfGPTS[4] = PIXEL_TO_GEO_X(nWidth, nHeight);
-        adfGPTS[5] = PIXEL_TO_GEO_Y(nWidth, nHeight);
+        adfGPTS[4] = APPLY_GT_X(adfGeoTransform, nWidth, nHeight);
+        adfGPTS[5] = APPLY_GT_Y(adfGeoTransform, nWidth, nHeight);
 
         /* Upper-right */
-        adfGPTS[6] = PIXEL_TO_GEO_X(nWidth, 0);
-        adfGPTS[7] = PIXEL_TO_GEO_Y(nWidth, 0);
+        adfGPTS[6] = APPLY_GT_X(adfGeoTransform, nWidth, 0);
+        adfGPTS[7] = APPLY_GT_Y(adfGeoTransform, nWidth, 0);
     }
 
     OGRSpatialReferenceH hSRS = OSRNewSpatialReference(pszWKT);
     if( hSRS == nullptr )
-        return 0;
+        return GDALPDFObjectNum();
+    // OSRSetAxisMappingStrategy(hSRS, OAMS_TRADITIONAL_GIS_ORDER); // Since GDAL 3.0
     OGRSpatialReferenceH hSRSGeog = OSRCloneGeogCS(hSRS);
     if( hSRSGeog == nullptr )
     {
         OSRDestroySpatialReference(hSRS);
-        return 0;
+        return GDALPDFObjectNum();
     }
+    // OSRSetAxisMappingStrategy(hSRSGeog, OAMS_TRADITIONAL_GIS_ORDER); // Since GDAL 3.0
     OGRCoordinateTransformationH hCT = OCTNewCoordinateTransformation( hSRS, hSRSGeog);
     if( hCT == nullptr )
     {
         OSRDestroySpatialReference(hSRS);
         OSRDestroySpatialReference(hSRSGeog);
-        return 0;
+        return GDALPDFObjectNum();
     }
 
     int bSuccess = TRUE;
@@ -736,7 +851,7 @@
         OSRDestroySpatialReference(hSRS);
         OSRDestroySpatialReference(hSRSGeog);
         OCTDestroyCoordinateTransformation(hCT);
-        return 0;
+        return GDALPDFObjectNum();
     }
 
     const char * pszAuthorityCode = OSRGetAuthorityCode( hSRS, nullptr );
@@ -760,13 +875,13 @@
     hCT = nullptr;
 
     if (pszESRIWKT == nullptr)
-        return 0;
+        return GDALPDFObjectNum();
 
-    int nViewportId = (bWriteViewport) ? AllocNewObject() : 0;
-    int nMeasureId = AllocNewObject();
-    int nGCSId = AllocNewObject();
+    auto nViewportId = (bWriteViewport) ? AllocNewObject() : GDALPDFObjectNum();
+    auto nMeasureId = AllocNewObject();
+    auto nGCSId = AllocNewObject();
 
-    if (nViewportId)
+    if (nViewportId.toBool())
     {
         StartObj(nViewportId);
         GDALPDFDictionaryRW oViewPortDict;
@@ -778,7 +893,7 @@
                                     .Add(dfLRPixel / dfUserUnit + psMargins->nLeft)
                                     .Add((nHeight - dfULLine) / dfUserUnit + psMargins->nBottom)))
                     .Add("Measure", nMeasureId, 0);
-        VSIFPrintfL(fp, "%s\n", oViewPortDict.Serialize().c_str());
+        VSIFPrintfL(m_fp, "%s\n", oViewPortDict.Serialize().c_str());
         EndObj();
     }
 
@@ -802,7 +917,7 @@
                                   Add(1).Add(0).
                                   Add(1).Add(1)))
                  .Add("GCS", nGCSId, 0);
-    VSIFPrintfL(fp, "%s\n", oMeasureDict.Serialize().c_str());
+    VSIFPrintfL(m_fp, "%s\n", oMeasureDict.Serialize().c_str());
     EndObj();
 
     StartObj(nGCSId);
@@ -811,12 +926,12 @@
             .Add("WKT", pszESRIWKT);
     if (nEPSGCode)
         oGCSDict.Add("EPSG", nEPSGCode);
-    VSIFPrintfL(fp, "%s\n", oGCSDict.Serialize().c_str());
+    VSIFPrintfL(m_fp, "%s\n", oGCSDict.Serialize().c_str());
     EndObj();
 
     CPLFree(pszESRIWKT);
 
-    return nViewportId ? nViewportId : nMeasureId;
+    return nViewportId.toBool() ? nViewportId : nMeasureId;
 }
 
 /************************************************************************/
@@ -1027,7 +1142,7 @@
 /*                   GDALPDFBuildOGC_BP_Projection()                    */
 /************************************************************************/
 
-static GDALPDFDictionaryRW* GDALPDFBuildOGC_BP_Projection(const OGRSpatialReference* poSRS)
+GDALPDFDictionaryRW* GDALPDFBaseWriter::GDALPDFBuildOGC_BP_Projection(const OGRSpatialReference* poSRS)
 {
 
     const char* pszProjectionOGCBP = "GEOGRAPHIC";
@@ -1180,7 +1295,7 @@
 /*                           WriteSRS_OGC_BP()                          */
 /************************************************************************/
 
-int GDALPDFWriter::WriteSRS_OGC_BP(GDALDataset* poSrcDS,
+GDALPDFObjectNum GDALPDFBaseWriter::WriteSRS_OGC_BP(GDALDataset* poSrcDS,
                                    double dfUserUnit,
                                    const char* pszNEATLINE,
                                    PDFMargins* psMargins)
@@ -1197,10 +1312,10 @@
         pszWKT = poSrcDS->GetGCPProjection();
 
     if( !bHasGT && pasGCPList == nullptr )
-        return 0;
+        return GDALPDFObjectNum();
 
     if( pszWKT == nullptr || EQUAL(pszWKT, "") )
-        return 0;
+        return GDALPDFObjectNum();
 
     if( !bHasGT )
     {
@@ -1217,14 +1332,15 @@
 
     OGRSpatialReferenceH hSRS = OSRNewSpatialReference(pszWKT);
     if( hSRS == nullptr )
-        return 0;
+        return GDALPDFObjectNum();
+    // OSRSetAxisMappingStrategy(hSRS, OAMS_TRADITIONAL_GIS_ORDER); // Since GDAL 3.0
 
-    const OGRSpatialReference* poSRS = (const OGRSpatialReference*)hSRS;
+    const OGRSpatialReference* poSRS = OGRSpatialReference::FromHandle(hSRS);
     GDALPDFDictionaryRW* poProjectionDict = GDALPDFBuildOGC_BP_Projection(poSRS);
     if (poProjectionDict == nullptr)
     {
         OSRDestroySpatialReference(hSRS);
-        return 0;
+        return GDALPDFObjectNum();
     }
 
     GDALPDFArrayRW* poNeatLineArray = nullptr;
@@ -1317,7 +1433,7 @@
         poNeatLineArray->Add((nHeight - 0) / dfUserUnit + psMargins->nBottom, TRUE);
     }
 
-    int nLGIDictId = AllocNewObject();
+    auto nLGIDictId = AllocNewObject();
     StartObj(nLGIDictId);
     GDALPDFDictionaryRW oLGIDict;
     oLGIDict.Add("Type", GDALPDFObjectRW::CreateName("LGIDict"))
@@ -1374,7 +1490,7 @@
     if( CPLTestBool( CPLGetConfigOption("GDAL_PDF_OGC_BP_WRITE_WKT", "TRUE") ) )
         poProjectionDict->Add("WKT", pszWKT);
 
-    VSIFPrintfL(fp, "%s\n", oLGIDict.Serialize().c_str());
+    VSIFPrintfL(m_fp, "%s\n", oLGIDict.Serialize().c_str());
     EndObj();
 
     OSRDestroySpatialReference(hSRS);
@@ -1403,8 +1519,8 @@
 /*                             SetInfo()                                */
 /************************************************************************/
 
-int GDALPDFWriter::SetInfo(GDALDataset* poSrcDS,
-                           char** papszOptions)
+GDALPDFObjectNum GDALPDFBaseWriter::SetInfo(GDALDataset* poSrcDS,
+                               char** papszOptions)
 {
     const char* pszAUTHOR = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "AUTHOR");
     const char* pszPRODUCER = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "PRODUCER");
@@ -1413,14 +1529,34 @@
     const char* pszSUBJECT = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "SUBJECT");
     const char* pszTITLE = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "TITLE");
     const char* pszKEYWORDS = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "KEYWORDS");
+    return SetInfo(pszAUTHOR,
+                   pszPRODUCER,
+                   pszCREATOR,
+                   pszCREATION_DATE,
+                   pszSUBJECT,
+                   pszTITLE,
+                   pszKEYWORDS);
+}
+
+/************************************************************************/
+/*                             SetInfo()                                */
+/************************************************************************/
 
+GDALPDFObjectNum GDALPDFBaseWriter::SetInfo(const char* pszAUTHOR,
+                               const char* pszPRODUCER,
+                               const char* pszCREATOR,
+                               const char* pszCREATION_DATE,
+                               const char* pszSUBJECT,
+                               const char* pszTITLE,
+                               const char* pszKEYWORDS)
+{
     if (pszAUTHOR == nullptr && pszPRODUCER == nullptr && pszCREATOR == nullptr && pszCREATION_DATE == nullptr &&
         pszSUBJECT == nullptr && pszTITLE == nullptr && pszKEYWORDS == nullptr)
-        return 0;
+        return GDALPDFObjectNum();
 
-    if (nInfoId == 0)
-        nInfoId = AllocNewObject();
-    StartObj(nInfoId, nInfoGen);
+    if (!m_nInfoId.toBool())
+        m_nInfoId = AllocNewObject();
+    StartObj(m_nInfoId, m_nInfoGen);
     GDALPDFDictionaryRW oDict;
     if (pszAUTHOR != nullptr)
         oDict.Add("Author", pszAUTHOR);
@@ -1436,86 +1572,90 @@
         oDict.Add("Title", pszTITLE);
     if (pszKEYWORDS != nullptr)
         oDict.Add("Keywords", pszKEYWORDS);
-    VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
+    VSIFPrintfL(m_fp, "%s\n", oDict.Serialize().c_str());
     EndObj();
 
-    return nInfoId;
+    return m_nInfoId;
 }
 
 /************************************************************************/
 /*                             SetXMP()                                 */
 /************************************************************************/
 
-int  GDALPDFWriter::SetXMP(GDALDataset* poSrcDS,
+GDALPDFObjectNum  GDALPDFBaseWriter::SetXMP(GDALDataset* poSrcDS,
                            const char* pszXMP)
 {
     if (pszXMP != nullptr && STARTS_WITH_CI(pszXMP, "NO"))
-        return 0;
+        return GDALPDFObjectNum();
     if (pszXMP != nullptr && pszXMP[0] == '\0')
-        return 0;
+        return GDALPDFObjectNum();
 
-    char** papszXMP = poSrcDS->GetMetadata("xml:XMP");
-    if (pszXMP == nullptr && papszXMP != nullptr && papszXMP[0] != nullptr)
-        pszXMP = papszXMP[0];
+    if( poSrcDS && pszXMP == nullptr )
+    {
+        char** papszXMP = poSrcDS->GetMetadata("xml:XMP");
+        if( papszXMP != nullptr && papszXMP[0] != nullptr)
+            pszXMP = papszXMP[0];
+    }
 
     if (pszXMP == nullptr)
-        return 0;
+        return GDALPDFObjectNum();
 
     CPLXMLNode* psNode = CPLParseXMLString(pszXMP);
     if (psNode == nullptr)
-        return 0;
+        return GDALPDFObjectNum();
     CPLDestroyXMLNode(psNode);
 
-    if(nXMPId == 0)
-        nXMPId = AllocNewObject();
-    StartObj(nXMPId, nXMPGen);
+    if(!m_nXMPId.toBool())
+        m_nXMPId = AllocNewObject();
+    StartObj(m_nXMPId, m_nXMPGen);
     GDALPDFDictionaryRW oDict;
     oDict.Add("Type", GDALPDFObjectRW::CreateName("Metadata"))
          .Add("Subtype", GDALPDFObjectRW::CreateName("XML"))
          .Add("Length", (int)strlen(pszXMP));
-    VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
-    VSIFPrintfL(fp, "stream\n");
-    VSIFPrintfL(fp, "%s\n", pszXMP);
-    VSIFPrintfL(fp, "endstream\n");
+    VSIFPrintfL(m_fp, "%s\n", oDict.Serialize().c_str());
+    VSIFPrintfL(m_fp, "stream\n");
+    VSIFPrintfL(m_fp, "%s\n", pszXMP);
+    VSIFPrintfL(m_fp, "endstream\n");
     EndObj();
-    return nXMPId;
+    return m_nXMPId;
 }
 
 /************************************************************************/
 /*                              WriteOCG()                              */
 /************************************************************************/
 
-int GDALPDFWriter::WriteOCG(const char* pszLayerName, int nParentId)
+GDALPDFObjectNum GDALPDFBaseWriter::WriteOCG(const char* pszLayerName,
+                                             const GDALPDFObjectNum& nParentId)
 {
     if (pszLayerName == nullptr || pszLayerName[0] == '\0')
-        return 0;
+        return GDALPDFObjectNum();
 
-    int nOGCId = AllocNewObject();
+    auto nOCGId = AllocNewObject();
 
     GDALPDFOCGDesc oOCGDesc;
-    oOCGDesc.nId = nOGCId;
+    oOCGDesc.nId = nOCGId;
     oOCGDesc.nParentId = nParentId;
     oOCGDesc.osLayerName = pszLayerName;
 
-    asOCGs.push_back(oOCGDesc);
+    m_asOCGs.push_back(oOCGDesc);
 
-    StartObj(nOGCId);
+    StartObj(nOCGId);
     {
         GDALPDFDictionaryRW oDict;
         oDict.Add("Type", GDALPDFObjectRW::CreateName("OCG"));
         oDict.Add("Name", pszLayerName);
-        VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
+        VSIFPrintfL(m_fp, "%s\n", oDict.Serialize().c_str());
     }
     EndObj();
 
-    return nOGCId;
+    return nOCGId;
 }
 
 /************************************************************************/
 /*                              StartPage()                             */
 /************************************************************************/
 
-int GDALPDFWriter::StartPage(GDALDataset* poClippingDS,
+bool GDALPDFWriter::StartPage(GDALDataset* poClippingDS,
                              double dfDPI,
                              bool bWriteUserUnit,
                              const char* pszGEO_ENCODING,
@@ -1532,31 +1672,31 @@
     double dfWidthInUserUnit = nWidth / dfUserUnit + psMargins->nLeft + psMargins->nRight;
     double dfHeightInUserUnit = nHeight / dfUserUnit + psMargins->nBottom + psMargins->nTop;
 
-    int nPageId = AllocNewObject();
-    asPageId.push_back(nPageId);
+    auto nPageId = AllocNewObject();
+    m_asPageId.push_back(nPageId);
 
-    int nContentId = AllocNewObject();
-    int nResourcesId = AllocNewObject();
+    auto nContentId = AllocNewObject();
+    auto nResourcesId = AllocNewObject();
 
-    int nAnnotsId = AllocNewObject();
+    auto nAnnotsId = AllocNewObject();
 
-    int bISO32000 = EQUAL(pszGEO_ENCODING, "ISO32000") ||
+    const bool bISO32000 = EQUAL(pszGEO_ENCODING, "ISO32000") ||
                     EQUAL(pszGEO_ENCODING, "BOTH");
-    int bOGC_BP   = EQUAL(pszGEO_ENCODING, "OGC_BP") ||
+    const bool bOGC_BP   = EQUAL(pszGEO_ENCODING, "OGC_BP") ||
                     EQUAL(pszGEO_ENCODING, "BOTH");
 
-    int nViewportId = 0;
+    GDALPDFObjectNum nViewportId;
     if( bISO32000 )
         nViewportId = WriteSRS_ISO32000(poClippingDS, dfUserUnit, pszNEATLINE, psMargins, TRUE);
 
-    int nLGIDictId = 0;
+    GDALPDFObjectNum nLGIDictId;
     if( bOGC_BP )
         nLGIDictId = WriteSRS_OGC_BP(poClippingDS, dfUserUnit, pszNEATLINE, psMargins);
 
     StartObj(nPageId);
     GDALPDFDictionaryRW oDictPage;
     oDictPage.Add("Type", GDALPDFObjectRW::CreateName("Page"))
-             .Add("Parent", nPageResourceId, 0)
+             .Add("Parent", m_nPageResourceId, 0)
              .Add("MediaBox", &((new GDALPDFArrayRW())
                                ->Add(0).Add(0).Add(dfWidthInUserUnit).Add(dfHeightInUserUnit)));
     if( bWriteUserUnit )
@@ -1573,12 +1713,12 @@
                          .Add("S", GDALPDFObjectRW::CreateName("Transparency"))
                          .Add("CS", GDALPDFObjectRW::CreateName("DeviceRGB"))));
     }
-    if (nViewportId)
+    if (nViewportId.toBool())
     {
         oDictPage.Add("VP", &((new GDALPDFArrayRW())
                                ->Add(nViewportId, 0)));
     }
-    if (nLGIDictId)
+    if (nLGIDictId.toBool())
     {
         oDictPage.Add("LGIDict", nLGIDictId, 0);
     }
@@ -1586,7 +1726,7 @@
     if (bHasOGRData)
         oDictPage.Add("StructParents", 0);
 
-    VSIFPrintfL(fp, "%s\n", oDictPage.Serialize().c_str());
+    VSIFPrintfL(m_fp, "%s\n", oDictPage.Serialize().c_str());
     EndObj();
 
     oPageContext.poClippingDS = poClippingDS;
@@ -1598,26 +1738,26 @@
     oPageContext.sMargins = *psMargins;
     oPageContext.eStreamCompressMethod = eStreamCompressMethod;
 
-    return TRUE;
+    return true;
 }
 
 /************************************************************************/
 /*                             WriteColorTable()                        */
 /************************************************************************/
 
-int GDALPDFWriter::WriteColorTable(GDALDataset* poSrcDS)
+GDALPDFObjectNum GDALPDFBaseWriter::WriteColorTable(GDALDataset* poSrcDS)
 {
     /* Does the source image has a color table ? */
     GDALColorTable* poCT = nullptr;
     if (poSrcDS->GetRasterCount() > 0)
         poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
-    int nColorTableId = 0;
+    GDALPDFObjectNum nColorTableId;
     if (poCT != nullptr && poCT->GetColorEntryCount() <= 256)
     {
         int nColors = poCT->GetColorEntryCount();
         nColorTableId = AllocNewObject();
 
-        int nLookupTableId = AllocNewObject();
+        auto nLookupTableId = AllocNewObject();
 
         /* Index object */
         StartObj(nColorTableId);
@@ -1627,7 +1767,7 @@
                   .Add(&((new GDALPDFArrayRW())->Add(GDALPDFObjectRW::CreateName("DeviceRGB"))))
                   .Add(nColors-1)
                   .Add(nLookupTableId, 0);
-            VSIFPrintfL(fp, "%s\n", oArray.Serialize().c_str());
+            VSIFPrintfL(m_fp, "%s\n", oArray.Serialize().c_str());
         }
         EndObj();
 
@@ -1636,9 +1776,9 @@
         {
             GDALPDFDictionaryRW oDict;
             oDict.Add("Length", nColors * 3);
-            VSIFPrintfL(fp, "%s %% Lookup table\n", oDict.Serialize().c_str());
+            VSIFPrintfL(m_fp, "%s %% Lookup table\n", oDict.Serialize().c_str());
         }
-        VSIFPrintfL(fp, "stream\n");
+        VSIFPrintfL(m_fp, "stream\n");
         GByte pabyLookup[768];
         for(int i=0;i<nColors;i++)
         {
@@ -1647,9 +1787,9 @@
             pabyLookup[3 * i + 1] = (GByte)poEntry->c2;
             pabyLookup[3 * i + 2] = (GByte)poEntry->c3;
         }
-        VSIFWriteL(pabyLookup, 3 * nColors, 1, fp);
-        VSIFPrintfL(fp, "\n");
-        VSIFPrintfL(fp, "endstream\n");
+        VSIFWriteL(pabyLookup, 3 * nColors, 1, m_fp);
+        VSIFPrintfL(m_fp, "\n");
+        VSIFPrintfL(m_fp, "endstream\n");
         EndObj();
     }
 
@@ -1660,7 +1800,7 @@
 /*                             WriteImagery()                           */
 /************************************************************************/
 
-int GDALPDFWriter::WriteImagery(GDALDataset* poDS,
+bool GDALPDFWriter::WriteImagery(GDALDataset* poDS,
                                 const char* pszLayerName,
                                 PDFCompressMethod eCompressMethod,
                                 int nPredictor,
@@ -1682,10 +1822,10 @@
     oRasterDesc.nOCGRasterId = WriteOCG(pszLayerName);
 
     /* Does the source image has a color table ? */
-    int nColorTableId = WriteColorTable(poDS);
+    auto nColorTableId = WriteColorTable(poDS);
 
-    int nXBlocks = (nWidth + nBlockXSize - 1) / nBlockXSize;
-    int nYBlocks = (nHeight + nBlockYSize - 1) / nBlockYSize;
+    int nXBlocks = DIV_ROUND_UP(nWidth, nBlockXSize);
+    int nYBlocks = DIV_ROUND_UP(nHeight, nBlockYSize);
     int nBlocks = nXBlocks * nYBlocks;
     int nBlockXOff, nBlockYOff;
     for(nBlockYOff = 0; nBlockYOff < nYBlocks; nBlockYOff ++)
@@ -1704,7 +1844,7 @@
             int nX = nBlockXOff * nBlockXSize;
             int nY = nBlockYOff * nBlockYSize;
 
-            int nImageId = WriteBlock(poDS,
+            auto nImageId = WriteBlock(poDS,
                                     nX,
                                     nY,
                                     nReqWidth, nReqHeight,
@@ -1718,8 +1858,8 @@
 
             GDALDestroyScaledProgress(pScaledData);
 
-            if (nImageId == 0)
-                return FALSE;
+            if (!nImageId.toBool())
+                return false;
 
             GDALPDFImageDesc oImageDesc;
             oImageDesc.nImageId = nImageId;
@@ -1734,14 +1874,14 @@
 
     oPageContext.asRasterDesc.push_back(oRasterDesc);
 
-    return TRUE;
+    return true;
 }
 
 /************************************************************************/
 /*                        WriteClippedImagery()                         */
 /************************************************************************/
 
-int GDALPDFWriter::WriteClippedImagery(
+bool GDALPDFWriter::WriteClippedImagery(
                                 GDALDataset* poDS,
                                 const char* pszLayerName,
                                 PDFCompressMethod eCompressMethod,
@@ -1769,9 +1909,7 @@
 
     if( dfClippingMaxY < dfClippingMinY )
     {
-        double dfTmp = dfClippingMinY;
-        dfClippingMinY = dfClippingMaxY;
-        dfClippingMaxY = dfTmp;
+        std::swap(dfClippingMinY, dfClippingMaxY);
     }
 
     /* Get current dataset dataset bounding-box */
@@ -1786,9 +1924,7 @@
 
     if( dfRasterMaxY < dfRasterMinY )
     {
-        double dfTmp = dfRasterMinY;
-        dfRasterMinY = dfRasterMaxY;
-        dfRasterMaxY = dfTmp;
+        std::swap(dfRasterMinY, dfRasterMaxY);
     }
 
     if( pfnProgress == nullptr )
@@ -1797,10 +1933,10 @@
     oRasterDesc.nOCGRasterId = WriteOCG(pszLayerName);
 
     /* Does the source image has a color table ? */
-    int nColorTableId = WriteColorTable(poDS);
+    auto nColorTableId = WriteColorTable(poDS);
 
-    int nXBlocks = (nWidth + nBlockXSize - 1) / nBlockXSize;
-    int nYBlocks = (nHeight + nBlockYSize - 1) / nBlockYSize;
+    int nXBlocks = DIV_ROUND_UP(nWidth, nBlockXSize);
+    int nYBlocks = DIV_ROUND_UP(nHeight, nBlockYSize);
     int nBlocks = nXBlocks * nYBlocks;
     int nBlockXOff, nBlockYOff;
     for(nBlockYOff = 0; nBlockYOff < nYBlocks; nBlockYOff ++)
@@ -1828,9 +1964,7 @@
 
             if( dfBlockMaxY < dfBlockMinY )
             {
-                double dfTmp = dfBlockMinY;
-                dfBlockMinY = dfBlockMaxY;
-                dfBlockMaxY = dfTmp;
+                std::swap(dfBlockMinY, dfBlockMaxY);
             }
 
             // Clip the extent of the block with the extent of the main raster.
@@ -1861,7 +1995,7 @@
 
                 if( nReqWidth > 0 && nReqHeight > 0)
                 {
-                    int nImageId = WriteBlock(poDS,
+                    auto nImageId = WriteBlock(poDS,
                                             nX,
                                             nY,
                                             nReqWidth, nReqHeight,
@@ -1873,10 +2007,10 @@
                                             GDALScaledProgress,
                                             pScaledData);
 
-                    if (nImageId == 0)
+                    if (!nImageId.toBool())
                     {
                         GDALDestroyScaledProgress(pScaledData);
-                        return FALSE;
+                        return false;
                     }
 
                     /* Compute the subwindow in image coordinates of the main raster corresponding */
@@ -1911,14 +2045,14 @@
 
     oPageContext.asRasterDesc.push_back(oRasterDesc);
 
-    return TRUE;
+    return true;
 }
 
 /************************************************************************/
 /*                          WriteOGRDataSource()                        */
 /************************************************************************/
 
-int GDALPDFWriter::WriteOGRDataSource(const char* pszOGRDataSource,
+bool GDALPDFWriter::WriteOGRDataSource(const char* pszOGRDataSource,
                                       const char* pszOGRDisplayField,
                                       const char* pszOGRDisplayLayerNames,
                                       const char* pszOGRLinkField,
@@ -1929,7 +2063,7 @@
 
     OGRDataSourceH hDS = OGROpen(pszOGRDataSource, 0, nullptr);
     if (hDS == nullptr)
-        return FALSE;
+        return false;
 
     int iObj = 0;
 
@@ -1957,7 +2091,7 @@
 
     CSLDestroy(papszLayerNames);
 
-    return TRUE;
+    return true;
 }
 
 /************************************************************************/
@@ -1970,9 +2104,9 @@
     GDALPDFLayerDesc osVectorDesc;
     osVectorDesc.osLayerName = osLayerName;
     osVectorDesc.bWriteOGRAttributes = bWriteOGRAttributes;
-    osVectorDesc.nOGCId = WriteOCG(osLayerName);
-    osVectorDesc.nFeatureLayerId = (bWriteOGRAttributes) ? AllocNewObject() : 0;
-    osVectorDesc.nOCGTextId = 0;
+    osVectorDesc.nOCGId = WriteOCG(osLayerName);
+    if( bWriteOGRAttributes )
+        osVectorDesc.nFeatureLayerId = AllocNewObject();
 
     return osVectorDesc;
 }
@@ -1999,14 +2133,14 @@
             poArray->Add(osVectorDesc.aUserPropertiesIds[i], 0);
         }
 
-        if (nStructTreeRootId == 0)
-            nStructTreeRootId = AllocNewObject();
+        if (!m_nStructTreeRootId.toBool())
+            m_nStructTreeRootId = AllocNewObject();
 
-        oDict.Add("P", nStructTreeRootId, 0);
+        oDict.Add("P", m_nStructTreeRootId, 0);
         oDict.Add("S", GDALPDFObjectRW::CreateName("Feature"));
         oDict.Add("T", osVectorDesc.osLayerName);
 
-        VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
+        VSIFPrintfL(m_fp, "%s\n", oDict.Serialize().c_str());
 
         EndObj();
     }
@@ -2035,10 +2169,29 @@
                                                   bWriteOGRAttributes);
     OGRLayerH hLyr = OGR_DS_GetLayer(hDS, iLayer);
 
-    const char* pszWKT = poClippingDS->GetProjectionRef();
-    OGRSpatialReferenceH hGDAL_SRS = nullptr;
-    if( pszWKT && pszWKT[0] != '\0' )
-        hGDAL_SRS = OSRNewSpatialReference(pszWKT);
+    const auto poLayerDefn = OGRLayer::FromHandle(hLyr)->GetLayerDefn();
+    for( int i = 0; i < poLayerDefn->GetFieldCount(); i++ )
+    {
+        const auto poFieldDefn = poLayerDefn->GetFieldDefn(i);
+        const char* pszName = poFieldDefn->GetNameRef();
+        osVectorDesc.aosIncludedFields.push_back(pszName);
+    }
+
+    const char* pWKT = poClippingDS->GetProjectionRef();
+    if( !pWKT || pWKT[0] == '\0')
+    {
+        CPLError(CE_Warning, CPLE_AppDefined,
+                 "Vector layer has a SRS set, but Raster layer has no SRS set. Assuming they are the same.");
+    }
+    OGRSpatialReference *pSRS = new OGRSpatialReference();
+    if( pSRS->importFromWkt(pWKT) != OGRERR_NONE )
+    {
+        CPLError(CE_Warning, CPLE_AppDefined,
+                 "Vector layer has a SRS set, but Raster layer has no SRS set. Assuming they are the same.");
+    }
+
+    OGRSpatialReferenceH hGDAL_SRS = OGRSpatialReference::ToHandle(
+        const_cast<OGRSpatialReference*>(pSRS));
     OGRSpatialReferenceH hOGR_SRS = OGR_L_GetSpatialRef(hLyr);
     OGRCoordinateTransformationH hCT = nullptr;
 
@@ -2075,7 +2228,6 @@
     }
 
     OGRFeatureH hFeat;
-    int iObjLayer = 0;
 
     while( (hFeat = OGR_L_GetNextFeature(hLyr)) != nullptr)
     {
@@ -2085,8 +2237,7 @@
                         pszOGRDisplayField,
                         pszOGRLinkField,
                         bWriteOGRAttributes,
-                        iObj,
-                        iObjLayer);
+                        iObj);
 
         OGR_F_Destroy(hFeat);
     }
@@ -2095,8 +2246,6 @@
 
     if( hCT != nullptr )
         OCTDestroyCoordinateTransformation(hCT);
-    if( hGDAL_SRS != nullptr )
-        OSRDestroySpatialReference(hGDAL_SRS);
 
     return TRUE;
 }
@@ -2105,7 +2254,8 @@
 /*                             DrawGeometry()                           */
 /************************************************************************/
 
-static void DrawGeometry(VSILFILE* fp, OGRGeometryH hGeom, double adfMatrix[4], int bPaint = TRUE)
+static void DrawGeometry(CPLString& osDS, OGRGeometryH hGeom,
+                         const double adfMatrix[4], bool bPaint = true)
 {
     switch(wkbFlatten(OGR_G_GetGeometryType(hGeom)))
     {
@@ -2116,10 +2266,10 @@
             {
                 double dfX = OGR_G_GetX(hGeom, i) * adfMatrix[1] + adfMatrix[0];
                 double dfY = OGR_G_GetY(hGeom, i) * adfMatrix[3] + adfMatrix[2];
-                VSIFPrintfL(fp, "%f %f %c\n", dfX, dfY, (i == 0) ? 'm' : 'l');
+                osDS += CPLOPrintf("%f %f %c\n", dfX, dfY, (i == 0) ? 'm' : 'l');
             }
             if (bPaint)
-                VSIFPrintfL(fp, "S\n");
+                osDS += CPLOPrintf("S\n");
             break;
         }
 
@@ -2128,11 +2278,11 @@
             int nParts = OGR_G_GetGeometryCount(hGeom);
             for(int i=0;i<nParts;i++)
             {
-                DrawGeometry(fp, OGR_G_GetGeometryRef(hGeom, i), adfMatrix, FALSE);
-                VSIFPrintfL(fp, "h\n");
+                DrawGeometry(osDS, OGR_G_GetGeometryRef(hGeom, i), adfMatrix, false);
+                osDS += CPLOPrintf("h\n");
             }
             if (bPaint)
-                VSIFPrintfL(fp, "b*\n");
+                osDS += CPLOPrintf("b*\n");
             break;
         }
 
@@ -2141,10 +2291,10 @@
             int nParts = OGR_G_GetGeometryCount(hGeom);
             for(int i=0;i<nParts;i++)
             {
-                DrawGeometry(fp, OGR_G_GetGeometryRef(hGeom, i), adfMatrix, FALSE);
+                DrawGeometry(osDS, OGR_G_GetGeometryRef(hGeom, i), adfMatrix, false);
             }
             if (bPaint)
-                VSIFPrintfL(fp, "S\n");
+                osDS += CPLOPrintf("S\n");
             break;
         }
 
@@ -2153,10 +2303,10 @@
             int nParts = OGR_G_GetGeometryCount(hGeom);
             for(int i=0;i<nParts;i++)
             {
-                DrawGeometry(fp, OGR_G_GetGeometryRef(hGeom, i), adfMatrix, FALSE);
+                DrawGeometry(osDS, OGR_G_GetGeometryRef(hGeom, i), adfMatrix, false);
             }
             if (bPaint)
-                VSIFPrintfL(fp, "b*\n");
+                osDS += CPLOPrintf("b*\n");
             break;
         }
 
@@ -2274,107 +2424,20 @@
 }
 
 /************************************************************************/
-/*                          WriteOGRFeature()                           */
+/*                          GetObjectStyle()                            */
 /************************************************************************/
 
-int GDALPDFWriter::WriteOGRFeature(GDALPDFLayerDesc& osVectorDesc,
-                                   OGRFeatureH hFeat,
-                                   OGRCoordinateTransformationH hCT,
-                                   const char* pszOGRDisplayField,
-                                   const char* pszOGRLinkField,
-                                   int bWriteOGRAttributes,
-                                   int& iObj,
-                                   int& iObjLayer)
+void GDALPDFBaseWriter::GetObjectStyle(const char* pszStyleString,
+                                       OGRFeatureH hFeat,
+                                       const double adfMatrix[4],
+                                       std::map<CPLString,GDALPDFImageDesc> oMapSymbolFilenameToDesc,
+                                       ObjectStyle& os)
 {
-    GDALDataset* const  poClippingDS = oPageContext.poClippingDS;
-    const int  nHeight = poClippingDS->GetRasterYSize();
-    const double dfUserUnit = oPageContext.dfDPI * USER_UNIT_IN_INCH;
-    double adfGeoTransform[6];
-    poClippingDS->GetGeoTransform(adfGeoTransform);
-
-    double adfMatrix[4];
-    adfMatrix[0] = - adfGeoTransform[0] / (adfGeoTransform[1] * dfUserUnit) + oPageContext.sMargins.nLeft;
-    adfMatrix[1] = 1.0 / (adfGeoTransform[1] * dfUserUnit);
-    adfMatrix[2] = - (adfGeoTransform[3] + adfGeoTransform[5] * nHeight) / (-adfGeoTransform[5] * dfUserUnit) + oPageContext.sMargins.nBottom;
-    adfMatrix[3] = 1.0 / (-adfGeoTransform[5] * dfUserUnit);
-
-    OGRGeometryH hGeom = OGR_F_GetGeometryRef(hFeat);
-    if (hGeom == nullptr)
-    {
-        return TRUE;
-    }
-
-    OGREnvelope sEnvelope;
-
-    if( hCT != nullptr )
-    {
-        /* Reproject */
-        if( OGR_G_Transform(hGeom, hCT) != OGRERR_NONE )
-        {
-            return TRUE;
-        }
-
-        OGREnvelope sRasterEnvelope;
-        sRasterEnvelope.MinX = adfGeoTransform[0];
-        sRasterEnvelope.MinY = adfGeoTransform[3]
-            + poClippingDS->GetRasterYSize() * adfGeoTransform[5];
-        sRasterEnvelope.MaxX = adfGeoTransform[0]
-            + poClippingDS->GetRasterXSize() * adfGeoTransform[1];
-        sRasterEnvelope.MaxY = adfGeoTransform[3];
-
-        // Check that the reprojected geometry intersects the raster envelope.
-        OGR_G_GetEnvelope(hGeom, &sEnvelope);
-        if( !(sRasterEnvelope.Intersects(sEnvelope)) )
-        {
-            return TRUE;
-        }
-    }
-    else
-    {
-        OGR_G_GetEnvelope(hGeom, &sEnvelope);
-    }
-
-    /* -------------------------------------------------------------- */
-    /*  Get style                                                     */
-    /* -------------------------------------------------------------- */
-    unsigned int nPenR = 0;
-    unsigned int nPenG = 0;
-    unsigned int nPenB = 0;
-    unsigned int nPenA = 255;
-    unsigned int nBrushR = 127;
-    unsigned int nBrushG = 127;
-    unsigned int nBrushB = 127;
-    unsigned int nBrushA = 127;
-    unsigned int nTextR = 0;
-    unsigned int nTextG = 0;
-    unsigned int nTextB = 0;
-    unsigned int nTextA = 255;
-    int bSymbolColorDefined = FALSE;
-    unsigned int nSymbolR = 0;
-    unsigned int nSymbolG = 0;
-    unsigned int nSymbolB = 0;
-    unsigned int nSymbolA = 255;
-    bool bHasPenBrushOrSymbol = false;
-    CPLString osTextFont;
-    bool bTextBold = false;
-    bool bTextItalic = false;
-    double dfTextSize = 12.0;
-    double dfTextAngle = 0.0;
-    double dfTextStretch = 1.0;
-    double dfTextDx = 0.0;
-    double dfTextDy = 0.0;
-    int nTextAnchor = 1;
-    double dfPenWidth = 1.0;
-    double dfSymbolSize = 5.0;
-    CPLString osDashArray;
-    CPLString osLabelText;
-    CPLString osSymbolId;
-    int nImageSymbolId = 0;
-    int nImageWidth = 0;
-    int nImageHeight = 0;
-
     OGRStyleMgrH hSM = OGR_SM_Create(nullptr);
-    OGR_SM_InitFromFeature(hSM, hFeat);
+    if( pszStyleString )
+        OGR_SM_InitStyleString(hSM, pszStyleString);
+    else
+        OGR_SM_InitFromFeature(hSM, hFeat);
     int nCount = OGR_SM_GetPartCount(hSM, nullptr);
     for(int iPart = 0; iPart < nCount; iPart++)
     {
@@ -2385,7 +2448,7 @@
             OGR_ST_SetUnit( hTool, OGRSTUMM, 1000.0 / adfMatrix[1] );
             if (OGR_ST_GetType(hTool) == OGRSTCPen)
             {
-                bHasPenBrushOrSymbol = true;
+                os.bHasPenBrushOrSymbol = true;
 
                 int bIsNull = TRUE;
                 const char* pszColor = OGR_ST_GetParamStr(hTool, OGRSTPenColor, &bIsNull);
@@ -2398,11 +2461,11 @@
                     int nVals = sscanf(pszColor,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,&nAlpha);
                     if (nVals >= 3)
                     {
-                        nPenR = nRed;
-                        nPenG = nGreen;
-                        nPenB = nBlue;
+                        os.nPenR = nRed;
+                        os.nPenG = nGreen;
+                        os.nPenB = nBlue;
                         if (nVals == 4)
-                            nPenA = nAlpha;
+                            os.nPenA = nAlpha;
                     }
                 }
 
@@ -2417,7 +2480,7 @@
                         {
                             double dfElement = CPLAtof(papszTokens[i]);
                             dfElement *= adfMatrix[1]; // should involve adfMatrix[3] too
-                            osDashArray += CPLSPrintf("%f ", dfElement);
+                            os.osDashArray += CPLSPrintf("%f ", dfElement);
                         }
                     }
                     CSLDestroy(papszTokens);
@@ -2426,11 +2489,11 @@
                 //OGRSTUnitId eUnit = OGR_ST_GetUnit(hTool);
                 double dfWidth = OGR_ST_GetParamDbl(hTool, OGRSTPenWidth, &bIsNull);
                 if (!bIsNull)
-                    dfPenWidth = dfWidth;
+                    os.dfPenWidth = dfWidth;
             }
             else if (OGR_ST_GetType(hTool) == OGRSTCBrush)
             {
-                bHasPenBrushOrSymbol = true;
+                os.bHasPenBrushOrSymbol = true;
 
                 int bIsNull;
                 const char* pszColor = OGR_ST_GetParamStr(hTool, OGRSTBrushFColor, &bIsNull);
@@ -2443,11 +2506,11 @@
                     int nVals = sscanf(pszColor,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,&nAlpha);
                     if (nVals >= 3)
                     {
-                        nBrushR = nRed;
-                        nBrushG = nGreen;
-                        nBrushB = nBlue;
+                        os.nBrushR = nRed;
+                        os.nBrushG = nGreen;
+                        os.nBrushB = nBlue;
                         if (nVals == 4)
-                            nBrushA = nAlpha;
+                            os.nBrushA = nAlpha;
                     }
                 }
             }
@@ -2457,21 +2520,21 @@
                 const char* pszStr = OGR_ST_GetParamStr(hTool, OGRSTLabelTextString, &bIsNull);
                 if (pszStr)
                 {
-                    osLabelText = pszStr;
+                    os.osLabelText = pszStr;
 
                     /* If the text is of the form {stuff}, then it means we want to fetch */
                     /* the value of the field "stuff" in the feature */
-                    if( !osLabelText.empty() && osLabelText[0] == '{' &&
-                        osLabelText.back() == '}' )
+                    if( !os.osLabelText.empty() && os.osLabelText[0] == '{' &&
+                        os.osLabelText.back() == '}' )
                     {
-                        osLabelText = pszStr + 1;
-                        osLabelText.resize(osLabelText.size() - 1);
+                        os.osLabelText = pszStr + 1;
+                        os.osLabelText.resize(os.osLabelText.size() - 1);
 
-                        int nIdxField = OGR_F_GetFieldIndex(hFeat, osLabelText);
+                        int nIdxField = OGR_F_GetFieldIndex(hFeat, os.osLabelText);
                         if( nIdxField >= 0 )
-                            osLabelText = OGR_F_GetFieldAsString(hFeat, nIdxField);
+                            os.osLabelText = OGR_F_GetFieldAsString(hFeat, nIdxField);
                         else
-                            osLabelText = "";
+                            os.osLabelText = "";
                     }
                 }
 
@@ -2485,77 +2548,77 @@
                     int nVals = sscanf(pszColor,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,&nAlpha);
                     if (nVals >= 3)
                     {
-                        nTextR = nRed;
-                        nTextG = nGreen;
-                        nTextB = nBlue;
+                        os.nTextR = nRed;
+                        os.nTextG = nGreen;
+                        os.nTextB = nBlue;
                         if (nVals == 4)
-                            nTextA = nAlpha;
+                            os.nTextA = nAlpha;
                     }
                 }
 
                 pszStr = OGR_ST_GetParamStr(hTool, OGRSTLabelFontName, &bIsNull);
                 if (pszStr && !bIsNull)
-                    osTextFont = pszStr;
+                    os.osTextFont = pszStr;
 
                 double dfVal = OGR_ST_GetParamDbl(hTool, OGRSTLabelSize, &bIsNull);
                 if (!bIsNull)
-                    dfTextSize = dfVal;
+                    os.dfTextSize = dfVal;
 
                 dfVal = OGR_ST_GetParamDbl(hTool, OGRSTLabelAngle, &bIsNull);
                 if (!bIsNull)
-                    dfTextAngle = dfVal * M_PI / 180.0;
+                    os.dfTextAngle = dfVal * M_PI / 180.0;
 
                 dfVal = OGR_ST_GetParamDbl(hTool, OGRSTLabelStretch, &bIsNull);
                 if (!bIsNull)
-                    dfTextStretch = dfVal / 100.0;
+                    os.dfTextStretch = dfVal / 100.0;
 
                 dfVal = OGR_ST_GetParamDbl(hTool, OGRSTLabelDx, &bIsNull);
                 if (!bIsNull)
-                    dfTextDx = dfVal;
+                    os.dfTextDx = dfVal;
 
                 dfVal = OGR_ST_GetParamDbl(hTool, OGRSTLabelDy, &bIsNull);
                 if (!bIsNull)
-                    dfTextDy = dfVal;
+                    os.dfTextDy = dfVal;
 
                 int nVal = OGR_ST_GetParamNum(hTool, OGRSTLabelAnchor, &bIsNull);
                 if (!bIsNull)
-                    nTextAnchor = nVal;
+                    os.nTextAnchor = nVal;
 
                 nVal = OGR_ST_GetParamNum(hTool, OGRSTLabelBold, &bIsNull);
                 if (!bIsNull)
-                    bTextBold = (nVal != 0);
+                    os.bTextBold = (nVal != 0);
 
                 nVal = OGR_ST_GetParamNum(hTool, OGRSTLabelItalic, &bIsNull);
                 if (!bIsNull)
-                    bTextItalic = (nVal != 0);
+                    os.bTextItalic = (nVal != 0);
             }
             else if (OGR_ST_GetType(hTool) == OGRSTCSymbol)
             {
-                bHasPenBrushOrSymbol = true;
+                os.bHasPenBrushOrSymbol = true;
 
                 int bIsNull;
                 const char* pszSymbolId = OGR_ST_GetParamStr(hTool, OGRSTSymbolId, &bIsNull);
                 if (pszSymbolId && !bIsNull)
                 {
-                    osSymbolId = pszSymbolId;
+                    os.osSymbolId = pszSymbolId;
 
                     if (strstr(pszSymbolId, "ogr-sym-") == nullptr)
                     {
-                        if (oMapSymbolFilenameToDesc.find(osSymbolId) == oMapSymbolFilenameToDesc.end())
+                        if (oMapSymbolFilenameToDesc.find(os.osSymbolId) == oMapSymbolFilenameToDesc.end())
                         {
                             CPLPushErrorHandler(CPLQuietErrorHandler);
-                            GDALDatasetH hImageDS = GDALOpen(osSymbolId, GA_ReadOnly);
+                            GDALDatasetH hImageDS = GDALOpen(os.osSymbolId, GA_ReadOnly);
                             CPLPopErrorHandler();
                             if (hImageDS != nullptr)
                             {
-                                nImageWidth = GDALGetRasterXSize(hImageDS);
-                                nImageHeight = GDALGetRasterYSize(hImageDS);
+                                os.nImageWidth = GDALGetRasterXSize(hImageDS);
+                                os.nImageHeight = GDALGetRasterYSize(hImageDS);
 
-                                nImageSymbolId = WriteBlock((GDALDataset*) hImageDS,
+                                os.nImageSymbolId = WriteBlock(GDALDataset::FromHandle(hImageDS),
                                                         0, 0,
-                                                        nImageWidth,
-                                                        nImageHeight,
-                                                        0,
+                                                        os.nImageWidth,
+                                                        os.nImageHeight,
+                                                        GDALPDFObjectNum(),
                                                         COMPRESS_DEFAULT,
                                                         0,
                                                         -1,
@@ -2566,19 +2629,19 @@
                             }
 
                             GDALPDFImageDesc oDesc;
-                            oDesc.nImageId = nImageSymbolId;
+                            oDesc.nImageId = os.nImageSymbolId;
                             oDesc.dfXOff = 0;
                             oDesc.dfYOff = 0;
-                            oDesc.dfXSize = nImageWidth;
-                            oDesc.dfYSize = nImageHeight;
-                            oMapSymbolFilenameToDesc[osSymbolId] = oDesc;
+                            oDesc.dfXSize = os.nImageWidth;
+                            oDesc.dfYSize = os.nImageHeight;
+                            oMapSymbolFilenameToDesc[os.osSymbolId] = oDesc;
                         }
                         else
                         {
-                            GDALPDFImageDesc& oDesc = oMapSymbolFilenameToDesc[osSymbolId];
-                            nImageSymbolId = oDesc.nImageId;
-                            nImageWidth = (int)oDesc.dfXSize;
-                            nImageHeight = (int)oDesc.dfYSize;
+                            GDALPDFImageDesc& oDesc = oMapSymbolFilenameToDesc[os.osSymbolId];
+                            os.nImageSymbolId = oDesc.nImageId;
+                            os.nImageWidth = (int)oDesc.dfXSize;
+                            os.nImageHeight = (int)oDesc.dfYSize;
                         }
                     }
                 }
@@ -2586,7 +2649,7 @@
                 double dfVal = OGR_ST_GetParamDbl(hTool, OGRSTSymbolSize, &bIsNull);
                 if (!bIsNull)
                 {
-                    dfSymbolSize = dfVal;
+                    os.dfSymbolSize = dfVal;
                 }
 
                 const char* pszColor = OGR_ST_GetParamStr(hTool, OGRSTSymbolColor, &bIsNull);
@@ -2599,12 +2662,12 @@
                     int nVals = sscanf(pszColor,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,&nAlpha);
                     if (nVals >= 3)
                     {
-                        bSymbolColorDefined = TRUE;
-                        nSymbolR = nRed;
-                        nSymbolG = nGreen;
-                        nSymbolB = nBlue;
+                        os.bSymbolColorDefined = TRUE;
+                        os.nSymbolR = nRed;
+                        os.nSymbolG = nGreen;
+                        os.nSymbolB = nBlue;
                         if (nVals == 4)
-                            nSymbolA = nAlpha;
+                            os.nSymbolA = nAlpha;
                     }
                 }
             }
@@ -2614,570 +2677,681 @@
     }
     OGR_SM_Destroy(hSM);
 
-    if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint && bSymbolColorDefined)
+    OGRGeometryH hGeom = OGR_F_GetGeometryRef(hFeat);
+    if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint &&
+        os.bSymbolColorDefined)
     {
-        nPenR = nSymbolR;
-        nPenG = nSymbolG;
-        nPenB = nSymbolB;
-        nPenA = nSymbolA;
-        nBrushR = nSymbolR;
-        nBrushG = nSymbolG;
-        nBrushB = nSymbolB;
-        nBrushA = nSymbolA;
+        os.nPenR = os.nSymbolR;
+        os.nPenG = os.nSymbolG;
+        os.nPenB = os.nSymbolB;
+        os.nPenA = os.nSymbolA;
+        os.nBrushR = os.nSymbolR;
+        os.nBrushG = os.nSymbolG;
+        os.nBrushB = os.nSymbolB;
+        os.nBrushA = os.nSymbolA;
     }
+}
 
-    double dfRadius = dfSymbolSize * dfUserUnit;
-
-    // For a POINT with only a LABEL style string and non-empty text, we do not
-    // output any geometry other than the text itself.
-    bool bLabelOnly = wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint &&
-        !bHasPenBrushOrSymbol && !osLabelText.empty();
+/************************************************************************/
+/*                           ComputeIntBBox()                           */
+/************************************************************************/
 
-    /* -------------------------------------------------------------- */
-    /*  Write object dictionary                                       */
-    /* -------------------------------------------------------------- */
-    if (!bLabelOnly)
+void GDALPDFBaseWriter::ComputeIntBBox(OGRGeometryH hGeom,
+                           const OGREnvelope& sEnvelope,
+                           const double adfMatrix[4],
+                           const GDALPDFWriter::ObjectStyle& os,
+                           double dfRadius,
+                           int& bboxXMin,
+                           int& bboxYMin,
+                           int& bboxXMax,
+                           int& bboxYMax)
+{
+    if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint && os.nImageSymbolId.toBool())
+    {
+        const double dfSemiWidth = (os.nImageWidth >= os.nImageHeight) ? dfRadius : dfRadius * os.nImageWidth / os.nImageHeight;
+        const double dfSemiHeight = (os.nImageWidth >= os.nImageHeight) ? dfRadius * os.nImageHeight / os.nImageWidth : dfRadius;
+        bboxXMin = (int)floor(sEnvelope.MinX * adfMatrix[1] + adfMatrix[0] - dfSemiWidth);
+        bboxYMin = (int)floor(sEnvelope.MinY * adfMatrix[3] + adfMatrix[2] - dfSemiHeight);
+        bboxXMax = (int)ceil(sEnvelope.MaxX * adfMatrix[1] + adfMatrix[0] + dfSemiWidth);
+        bboxYMax = (int)ceil(sEnvelope.MaxY * adfMatrix[3] + adfMatrix[2] + dfSemiHeight);
+    }
+    else
     {
-        int nObjectId = AllocNewObject();
-        int nObjectLengthId = AllocNewObject();
-
-        osVectorDesc.aIds.push_back(nObjectId);
-
-        int bboxXMin, bboxYMin, bboxXMax, bboxYMax;
-        if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint && nImageSymbolId != 0)
+        double dfMargin = os.dfPenWidth;
+        if( wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint )
         {
-            bboxXMin = (int)floor(sEnvelope.MinX * adfMatrix[1] + adfMatrix[0] - nImageWidth / 2);
-            bboxYMin = (int)floor(sEnvelope.MinY * adfMatrix[3] + adfMatrix[2] - nImageHeight / 2);
-            bboxXMax = (int)ceil(sEnvelope.MaxX * adfMatrix[1] + adfMatrix[0] + nImageWidth / 2);
-            bboxYMax = (int)ceil(sEnvelope.MaxY * adfMatrix[3] + adfMatrix[2] + nImageHeight / 2);
+            if (os.osSymbolId == "ogr-sym-6" ||
+                os.osSymbolId == "ogr-sym-7")
+            {
+                const double dfSqrt3 = 1.73205080757;
+                dfMargin += dfRadius * 2 * dfSqrt3 / 3;
+            }
+            else
+                dfMargin += dfRadius;
         }
-        else
+        bboxXMin = (int)floor(sEnvelope.MinX * adfMatrix[1] + adfMatrix[0] - dfMargin);
+        bboxYMin = (int)floor(sEnvelope.MinY * adfMatrix[3] + adfMatrix[2] - dfMargin);
+        bboxXMax = (int)ceil(sEnvelope.MaxX * adfMatrix[1] + adfMatrix[0] + dfMargin);
+        bboxYMax = (int)ceil(sEnvelope.MaxY * adfMatrix[3] + adfMatrix[2] + dfMargin);
+    }
+}
+
+/************************************************************************/
+/*                              WriteLink()                             */
+/************************************************************************/
+
+GDALPDFObjectNum GDALPDFBaseWriter::WriteLink(OGRFeatureH hFeat,
+                              const char* pszOGRLinkField,
+                              const double adfMatrix[4],
+                              int bboxXMin,
+                              int bboxYMin,
+                              int bboxXMax,
+                              int bboxYMax)
+{
+    GDALPDFObjectNum nAnnotId;
+    int iField = -1;
+    const char* pszLinkVal = nullptr;
+    if (pszOGRLinkField != nullptr &&
+        (iField = OGR_FD_GetFieldIndex(OGR_F_GetDefnRef(hFeat), pszOGRLinkField)) >= 0 &&
+        OGR_F_IsFieldSetAndNotNull(hFeat, iField) &&
+        strcmp((pszLinkVal = OGR_F_GetFieldAsString(hFeat, iField)), "") != 0)
+    {
+        nAnnotId = AllocNewObject();
+        StartObj(nAnnotId);
         {
-            double dfMargin = dfPenWidth;
-            if( wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint )
-            {
-                if (osSymbolId == "ogr-sym-6" ||
-                    osSymbolId == "ogr-sym-7")
+            GDALPDFDictionaryRW oDict;
+            oDict.Add("Type", GDALPDFObjectRW::CreateName("Annot"));
+            oDict.Add("Subtype", GDALPDFObjectRW::CreateName("Link"));
+            oDict.Add("Rect", &(new GDALPDFArrayRW())->Add(bboxXMin).Add(bboxYMin).Add(bboxXMax).Add(bboxYMax));
+            oDict.Add("A", &(new GDALPDFDictionaryRW())->
+                Add("S", GDALPDFObjectRW::CreateName("URI")).
+                Add("URI", pszLinkVal));
+            oDict.Add("BS", &(new GDALPDFDictionaryRW())->
+                Add("Type", GDALPDFObjectRW::CreateName("Border")).
+                Add("S", GDALPDFObjectRW::CreateName("S")).
+                Add("W", 0));
+            oDict.Add("Border", &(new GDALPDFArrayRW())->Add(0).Add(0).Add(0));
+            oDict.Add("H", GDALPDFObjectRW::CreateName("I"));
+
+            OGRGeometryH hGeom = OGR_F_GetGeometryRef(hFeat);
+            if( wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPolygon &&
+                OGR_G_GetGeometryCount(hGeom) == 1 )
+            {
+                OGRGeometryH hSubGeom = OGR_G_GetGeometryRef(hGeom, 0);
+                int nPoints = OGR_G_GetPointCount(hSubGeom);
+                if( nPoints == 4 || nPoints == 5 )
                 {
-                    const double dfSqrt3 = 1.73205080757;
-                    dfMargin += dfRadius * 2 * dfSqrt3 / 3;
-                }
-                else
-                    dfMargin += dfRadius;
-            }
-            bboxXMin = (int)floor(sEnvelope.MinX * adfMatrix[1] + adfMatrix[0] - dfMargin);
-            bboxYMin = (int)floor(sEnvelope.MinY * adfMatrix[3] + adfMatrix[2] - dfMargin);
-            bboxXMax = (int)ceil(sEnvelope.MaxX * adfMatrix[1] + adfMatrix[0] + dfMargin);
-            bboxYMax = (int)ceil(sEnvelope.MaxY * adfMatrix[3] + adfMatrix[2] + dfMargin);
-        }
-
-        int iField = -1;
-        const char* pszLinkVal = nullptr;
-        if (pszOGRLinkField != nullptr &&
-            (iField = OGR_FD_GetFieldIndex(OGR_F_GetDefnRef(hFeat), pszOGRLinkField)) >= 0 &&
-            OGR_F_IsFieldSetAndNotNull(hFeat, iField) &&
-            strcmp((pszLinkVal = OGR_F_GetFieldAsString(hFeat, iField)), "") != 0)
-        {
-            int nAnnotId = AllocNewObject();
-            oPageContext.anAnnotationsId.push_back(nAnnotId);
-            StartObj(nAnnotId);
-            {
-                GDALPDFDictionaryRW oDict;
-                oDict.Add("Type", GDALPDFObjectRW::CreateName("Annot"));
-                oDict.Add("Subtype", GDALPDFObjectRW::CreateName("Link"));
-                oDict.Add("Rect", &(new GDALPDFArrayRW())->Add(bboxXMin).Add(bboxYMin).Add(bboxXMax).Add(bboxYMax));
-                oDict.Add("A", &(new GDALPDFDictionaryRW())->
-                    Add("S", GDALPDFObjectRW::CreateName("URI")).
-                    Add("URI", pszLinkVal));
-                oDict.Add("BS", &(new GDALPDFDictionaryRW())->
-                    Add("Type", GDALPDFObjectRW::CreateName("Border")).
-                    Add("S", GDALPDFObjectRW::CreateName("S")).
-                    Add("W", 0));
-                oDict.Add("Border", &(new GDALPDFArrayRW())->Add(0).Add(0).Add(0));
-                oDict.Add("H", GDALPDFObjectRW::CreateName("I"));
-
-                if( wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPolygon &&
-                    OGR_G_GetGeometryCount(hGeom) == 1 )
-                {
-                    OGRGeometryH hSubGeom = OGR_G_GetGeometryRef(hGeom, 0);
-                    int nPoints = OGR_G_GetPointCount(hSubGeom);
-                    if( nPoints == 4 || nPoints == 5 )
+                    std::vector<double> adfX, adfY;
+                    for(int i=0;i<nPoints;i++)
                     {
-                        std::vector<double> adfX, adfY;
-                        for(int i=0;i<nPoints;i++)
-                        {
-                            double dfX = OGR_G_GetX(hSubGeom, i) * adfMatrix[1] + adfMatrix[0];
-                            double dfY = OGR_G_GetY(hSubGeom, i) * adfMatrix[3] + adfMatrix[2];
-                            adfX.push_back(dfX);
-                            adfY.push_back(dfY);
-                        }
-                        if( nPoints == 4 )
-                        {
-                            oDict.Add("QuadPoints", &(new GDALPDFArrayRW())->
-                                Add(adfX[0]).Add(adfY[0]).
-                                Add(adfX[1]).Add(adfY[1]).
-                                Add(adfX[2]).Add(adfY[2]).
-                                Add(adfX[0]).Add(adfY[0]));
-                        }
-                        else if( nPoints == 5 )
-                        {
-                            oDict.Add("QuadPoints", &(new GDALPDFArrayRW())->
-                                Add(adfX[0]).Add(adfY[0]).
-                                Add(adfX[1]).Add(adfY[1]).
-                                Add(adfX[2]).Add(adfY[2]).
-                                Add(adfX[3]).Add(adfY[3]));
-                        }
+                        double dfX = OGR_G_GetX(hSubGeom, i) * adfMatrix[1] + adfMatrix[0];
+                        double dfY = OGR_G_GetY(hSubGeom, i) * adfMatrix[3] + adfMatrix[2];
+                        adfX.push_back(dfX);
+                        adfY.push_back(dfY);
+                    }
+                    if( nPoints == 4 )
+                    {
+                        oDict.Add("QuadPoints", &(new GDALPDFArrayRW())->
+                            Add(adfX[0]).Add(adfY[0]).
+                            Add(adfX[1]).Add(adfY[1]).
+                            Add(adfX[2]).Add(adfY[2]).
+                            Add(adfX[0]).Add(adfY[0]));
+                    }
+                    else if( nPoints == 5 )
+                    {
+                        oDict.Add("QuadPoints", &(new GDALPDFArrayRW())->
+                            Add(adfX[0]).Add(adfY[0]).
+                            Add(adfX[1]).Add(adfY[1]).
+                            Add(adfX[2]).Add(adfY[2]).
+                            Add(adfX[3]).Add(adfY[3]));
                     }
                 }
-
-                VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
             }
-            EndObj();
+
+            VSIFPrintfL(m_fp, "%s\n", oDict.Serialize().c_str());
         }
+        EndObj();
+    }
+    return nAnnotId;
+}
+
+/************************************************************************/
+/*                        GenerateDrawingStream()                       */
+/************************************************************************/
 
-        StartObj(nObjectId);
+CPLString GDALPDFBaseWriter::GenerateDrawingStream(OGRGeometryH hGeom,
+                                                   const double adfMatrix[4],
+                                                   ObjectStyle& os,
+                                                   double dfRadius)
+{
+    CPLString osDS;
+
+    if (!os.nImageSymbolId.toBool())
+    {
+        osDS += CPLOPrintf("%f w\n"
+                        "0 J\n"
+                        "0 j\n"
+                        "10 M\n"
+                        "[%s]0 d\n",
+                        os.dfPenWidth,
+                        os.osDashArray.c_str());
+
+        osDS += CPLOPrintf("%f %f %f RG\n", os.nPenR / 255.0, os.nPenG / 255.0, os.nPenB / 255.0);
+        osDS += CPLOPrintf("%f %f %f rg\n", os.nBrushR / 255.0, os.nBrushG / 255.0, os.nBrushB / 255.0);
+    }
+
+    if ((os.bHasPenBrushOrSymbol || os.osLabelText.empty()) &&
+        wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint)
+    {
+        double dfX = OGR_G_GetX(hGeom, 0) * adfMatrix[1] + adfMatrix[0];
+        double dfY = OGR_G_GetY(hGeom, 0) * adfMatrix[3] + adfMatrix[2];
+
+        if (os.nImageSymbolId.toBool())
+        {
+            const double dfSemiWidth = (os.nImageWidth >= os.nImageHeight) ? dfRadius : dfRadius * os.nImageWidth / os.nImageHeight;
+            const double dfSemiHeight = (os.nImageWidth >= os.nImageHeight) ? dfRadius * os.nImageHeight / os.nImageWidth : dfRadius;
+            osDS += CPLOPrintf("%f 0 0 %f %f %f cm\n",
+                        2 * dfSemiWidth,
+                        2 * dfSemiHeight,
+                        dfX - dfSemiWidth,
+                        dfY - dfSemiHeight);
+            osDS += CPLOPrintf("/SymImage%d Do\n", os.nImageSymbolId.toInt());
+        }
+        else if (os.osSymbolId == "")
+            os.osSymbolId = "ogr-sym-3"; /* symbol by default */
+        else if ( !(os.osSymbolId == "ogr-sym-0" ||
+                    os.osSymbolId == "ogr-sym-1" ||
+                    os.osSymbolId == "ogr-sym-2" ||
+                    os.osSymbolId == "ogr-sym-3" ||
+                    os.osSymbolId == "ogr-sym-4" ||
+                    os.osSymbolId == "ogr-sym-5" ||
+                    os.osSymbolId == "ogr-sym-6" ||
+                    os.osSymbolId == "ogr-sym-7" ||
+                    os.osSymbolId == "ogr-sym-8" ||
+                    os.osSymbolId == "ogr-sym-9") )
+        {
+            CPLDebug("PDF", "Unhandled symbol id : %s. Using ogr-sym-3 instead", os.osSymbolId.c_str());
+            os.osSymbolId = "ogr-sym-3";
+        }
+
+        if (os.osSymbolId == "ogr-sym-0") /* cross (+)  */
+        {
+            osDS += CPLOPrintf("%f %f m\n", dfX - dfRadius, dfY);
+            osDS += CPLOPrintf("%f %f l\n", dfX + dfRadius, dfY);
+            osDS += CPLOPrintf("%f %f m\n", dfX, dfY - dfRadius);
+            osDS += CPLOPrintf("%f %f l\n", dfX, dfY + dfRadius);
+            osDS += CPLOPrintf("S\n");
+        }
+        else if (os.osSymbolId == "ogr-sym-1") /* diagcross (X) */
+        {
+            osDS += CPLOPrintf("%f %f m\n", dfX - dfRadius, dfY - dfRadius);
+            osDS += CPLOPrintf("%f %f l\n", dfX + dfRadius, dfY + dfRadius);
+            osDS += CPLOPrintf("%f %f m\n", dfX - dfRadius, dfY + dfRadius);
+            osDS += CPLOPrintf("%f %f l\n", dfX + dfRadius, dfY - dfRadius);
+            osDS += CPLOPrintf("S\n");
+        }
+        else if (os.osSymbolId == "ogr-sym-2" ||
+                    os.osSymbolId == "ogr-sym-3") /* circle */
+        {
+            /* See http://www.whizkidtech.redprince.net/bezier/circle/kappa/ */
+            const double dfKappa = 0.5522847498;
+
+            osDS += CPLOPrintf("%f %f m\n", dfX - dfRadius, dfY);
+            osDS += CPLOPrintf("%f %f %f %f %f %f c\n",
+                        dfX - dfRadius, dfY - dfRadius * dfKappa,
+                        dfX - dfRadius * dfKappa, dfY - dfRadius,
+                        dfX, dfY - dfRadius);
+            osDS += CPLOPrintf("%f %f %f %f %f %f c\n",
+                        dfX + dfRadius * dfKappa, dfY - dfRadius,
+                        dfX + dfRadius, dfY - dfRadius * dfKappa,
+                        dfX + dfRadius, dfY);
+            osDS += CPLOPrintf("%f %f %f %f %f %f c\n",
+                        dfX + dfRadius, dfY + dfRadius * dfKappa,
+                        dfX + dfRadius * dfKappa, dfY + dfRadius,
+                        dfX, dfY + dfRadius);
+            osDS += CPLOPrintf("%f %f %f %f %f %f c\n",
+                        dfX - dfRadius * dfKappa, dfY + dfRadius,
+                        dfX - dfRadius, dfY + dfRadius * dfKappa,
+                        dfX - dfRadius, dfY);
+            if (os.osSymbolId == "ogr-sym-2")
+                osDS += CPLOPrintf("s\n"); /* not filled */
+            else
+                osDS += CPLOPrintf("b*\n"); /* filled */
+        }
+        else if (os.osSymbolId == "ogr-sym-4" ||
+                    os.osSymbolId == "ogr-sym-5") /* square */
         {
-            GDALPDFDictionaryRW oDict;
-            GDALPDFArrayRW* poBBOX = new GDALPDFArrayRW();
-            poBBOX->Add(bboxXMin).Add(bboxYMin).Add(bboxXMax). Add(bboxYMax);
-            oDict.Add("Length", nObjectLengthId, 0)
-                .Add("Type", GDALPDFObjectRW::CreateName("XObject"))
-                .Add("BBox", poBBOX)
-                .Add("Subtype", GDALPDFObjectRW::CreateName("Form"));
-            if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
-            {
-                oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode"));
+            osDS += CPLOPrintf("%f %f m\n", dfX - dfRadius, dfY + dfRadius);
+            osDS += CPLOPrintf("%f %f l\n", dfX + dfRadius, dfY + dfRadius);
+            osDS += CPLOPrintf("%f %f l\n", dfX + dfRadius, dfY - dfRadius);
+            osDS += CPLOPrintf("%f %f l\n", dfX - dfRadius, dfY - dfRadius);
+            if (os.osSymbolId == "ogr-sym-4")
+                osDS += CPLOPrintf("s\n"); /* not filled */
+            else
+                osDS += CPLOPrintf("b*\n"); /* filled */
+        }
+        else if (os.osSymbolId == "ogr-sym-6" ||
+                    os.osSymbolId == "ogr-sym-7") /* triangle */
+        {
+            const double dfSqrt3 = 1.73205080757;
+            osDS += CPLOPrintf("%f %f m\n", dfX - dfRadius, dfY - dfRadius * dfSqrt3 / 3);
+            osDS += CPLOPrintf("%f %f l\n", dfX, dfY + 2 * dfRadius * dfSqrt3 / 3);
+            osDS += CPLOPrintf("%f %f l\n", dfX + dfRadius, dfY - dfRadius * dfSqrt3 / 3);
+            if (os.osSymbolId == "ogr-sym-6")
+                osDS += CPLOPrintf("s\n"); /* not filled */
+            else
+                osDS += CPLOPrintf("b*\n"); /* filled */
+        }
+        else if (os.osSymbolId == "ogr-sym-8" ||
+                    os.osSymbolId == "ogr-sym-9") /* star */
+        {
+            const double dfSin18divSin126 = 0.38196601125;
+            osDS += CPLOPrintf("%f %f m\n", dfX, dfY + dfRadius);
+            for(int i=1; i<10;i++)
+            {
+                double dfFactor = ((i % 2) == 1) ? dfSin18divSin126 : 1.0;
+                osDS += CPLOPrintf("%f %f l\n",
+                            dfX + cos(M_PI / 2 - i * M_PI * 36 / 180) * dfRadius * dfFactor,
+                            dfY + sin(M_PI / 2 - i * M_PI * 36 / 180) * dfRadius * dfFactor);
             }
+            if (os.osSymbolId == "ogr-sym-8")
+                osDS += CPLOPrintf("s\n"); /* not filled */
+            else
+                osDS += CPLOPrintf("b*\n"); /* filled */
+        }
+    }
+    else
+    {
+        DrawGeometry(osDS, hGeom, adfMatrix);
+    }
 
-            GDALPDFDictionaryRW* poGS1 = new GDALPDFDictionaryRW();
-            poGS1->Add("Type", GDALPDFObjectRW::CreateName("ExtGState"));
-            if (nPenA != 255)
-                poGS1->Add("CA", (nPenA == 127 || nPenA == 128) ? 0.5 : nPenA / 255.0);
-            if (nBrushA != 255)
-                poGS1->Add("ca", (nBrushA == 127 || nBrushA == 128) ? 0.5 : nBrushA / 255.0 );
-
-            GDALPDFDictionaryRW* poExtGState = new GDALPDFDictionaryRW();
-            poExtGState->Add("GS1", poGS1);
-
-            GDALPDFDictionaryRW* poResources = new GDALPDFDictionaryRW();
-            poResources->Add("ExtGState", poExtGState);
-
-            if( nImageSymbolId != 0 )
-            {
-                GDALPDFDictionaryRW* poDictXObject = new GDALPDFDictionaryRW();
-                poResources->Add("XObject", poDictXObject);
+    return osDS;
+}
 
-                poDictXObject->Add(CPLSPrintf("SymImage%d", nImageSymbolId), nImageSymbolId, 0);
-            }
+/************************************************************************/
+/*                          WriteAttributes()                           */
+/************************************************************************/
 
-            oDict.Add("Resources", poResources);
+GDALPDFObjectNum GDALPDFBaseWriter::WriteAttributes(
+        OGRFeatureH hFeat,
+        const std::vector<CPLString>& aosIncludedFields,
+        const char* pszOGRDisplayField,
+        int nMCID,
+        const GDALPDFObjectNum& oParent,
+        const GDALPDFObjectNum& oPage,
+        CPLString& osOutFeatureName)
+{
+
+    int iField = -1;
+    if (pszOGRDisplayField )
+        iField = OGR_FD_GetFieldIndex(OGR_F_GetDefnRef(hFeat), pszOGRDisplayField);
+    if( iField >= 0 )
+        osOutFeatureName = OGR_F_GetFieldAsString(hFeat, iField);
+    else
+        osOutFeatureName = CPLSPrintf("feature" CPL_FRMT_GIB, OGR_F_GetFID(hFeat));
 
-            VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
-        }
+    auto nFeatureUserProperties = AllocNewObject();
+    StartObj(nFeatureUserProperties);
 
-        /* -------------------------------------------------------------- */
-        /*  Write object stream                                           */
-        /* -------------------------------------------------------------- */
-        VSIFPrintfL(fp, "stream\n");
+    GDALPDFDictionaryRW oDict;
 
-        vsi_l_offset nStreamStart = VSIFTellL(fp);
+    GDALPDFDictionaryRW* poDictA = new GDALPDFDictionaryRW();
+    oDict.Add("A", poDictA);
+    poDictA->Add("O", GDALPDFObjectRW::CreateName("UserProperties"));
 
-        VSILFILE* fpGZip = nullptr;
-        VSILFILE* fpBack = fp;
-        if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
-        {
-            fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE );
-            fp = fpGZip;
-        }
-
-        VSIFPrintfL(fp, "q\n");
-
-        VSIFPrintfL(fp, "/GS1 gs\n");
-
-        if (nImageSymbolId == 0)
-        {
-            VSIFPrintfL(fp, "%f w\n"
-                            "0 J\n"
-                            "0 j\n"
-                            "10 M\n"
-                            "[%s]0 d\n",
-                            dfPenWidth,
-                            osDashArray.c_str());
-
-            VSIFPrintfL(fp, "%f %f %f RG\n", nPenR / 255.0, nPenG / 255.0, nPenB / 255.0);
-            VSIFPrintfL(fp, "%f %f %f rg\n", nBrushR / 255.0, nBrushG / 255.0, nBrushB / 255.0);
-        }
-
-        if ((bHasPenBrushOrSymbol || osLabelText.empty()) &&
-            wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint)
-        {
-            double dfX = OGR_G_GetX(hGeom, 0) * adfMatrix[1] + adfMatrix[0];
-            double dfY = OGR_G_GetY(hGeom, 0) * adfMatrix[3] + adfMatrix[2];
-
-            if (nImageSymbolId != 0)
-            {
-                VSIFPrintfL(fp, "%d 0 0 %d %f %f cm\n",
-                            nImageWidth, nImageHeight,
-                            dfX - nImageWidth / 2, dfY - nImageHeight / 2);
-                VSIFPrintfL(fp, "/SymImage%d Do\n", nImageSymbolId);
-            }
-            else if (osSymbolId == "")
-                osSymbolId = "ogr-sym-3"; /* symbol by default */
-            else if ( !(osSymbolId == "ogr-sym-0" ||
-                        osSymbolId == "ogr-sym-1" ||
-                        osSymbolId == "ogr-sym-2" ||
-                        osSymbolId == "ogr-sym-3" ||
-                        osSymbolId == "ogr-sym-4" ||
-                        osSymbolId == "ogr-sym-5" ||
-                        osSymbolId == "ogr-sym-6" ||
-                        osSymbolId == "ogr-sym-7" ||
-                        osSymbolId == "ogr-sym-8" ||
-                        osSymbolId == "ogr-sym-9") )
-            {
-                CPLDebug("PDF", "Unhandled symbol id : %s. Using ogr-sym-3 instead", osSymbolId.c_str());
-                osSymbolId = "ogr-sym-3";
-            }
-
-            if (osSymbolId == "ogr-sym-0") /* cross (+)  */
-            {
-                VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY);
-                VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY);
-                VSIFPrintfL(fp, "%f %f m\n", dfX, dfY - dfRadius);
-                VSIFPrintfL(fp, "%f %f l\n", dfX, dfY + dfRadius);
-                VSIFPrintfL(fp, "S\n");
-            }
-            else if (osSymbolId == "ogr-sym-1") /* diagcross (X) */
-            {
-                VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY - dfRadius);
-                VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY + dfRadius);
-                VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY + dfRadius);
-                VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY - dfRadius);
-                VSIFPrintfL(fp, "S\n");
-            }
-            else if (osSymbolId == "ogr-sym-2" ||
-                     osSymbolId == "ogr-sym-3") /* circle */
-            {
-                /* See http://www.whizkidtech.redprince.net/bezier/circle/kappa/ */
-                const double dfKappa = 0.5522847498;
-
-                VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY);
-                VSIFPrintfL(fp, "%f %f %f %f %f %f c\n",
-                            dfX - dfRadius, dfY - dfRadius * dfKappa,
-                            dfX - dfRadius * dfKappa, dfY - dfRadius,
-                            dfX, dfY - dfRadius);
-                VSIFPrintfL(fp, "%f %f %f %f %f %f c\n",
-                            dfX + dfRadius * dfKappa, dfY - dfRadius,
-                            dfX + dfRadius, dfY - dfRadius * dfKappa,
-                            dfX + dfRadius, dfY);
-                VSIFPrintfL(fp, "%f %f %f %f %f %f c\n",
-                            dfX + dfRadius, dfY + dfRadius * dfKappa,
-                            dfX + dfRadius * dfKappa, dfY + dfRadius,
-                            dfX, dfY + dfRadius);
-                VSIFPrintfL(fp, "%f %f %f %f %f %f c\n",
-                            dfX - dfRadius * dfKappa, dfY + dfRadius,
-                            dfX - dfRadius, dfY + dfRadius * dfKappa,
-                            dfX - dfRadius, dfY);
-                if (osSymbolId == "ogr-sym-2")
-                    VSIFPrintfL(fp, "s\n"); /* not filled */
-                else
-                    VSIFPrintfL(fp, "b*\n"); /* filled */
-            }
-            else if (osSymbolId == "ogr-sym-4" ||
-                     osSymbolId == "ogr-sym-5") /* square */
-            {
-                VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY + dfRadius);
-                VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY + dfRadius);
-                VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY - dfRadius);
-                VSIFPrintfL(fp, "%f %f l\n", dfX - dfRadius, dfY - dfRadius);
-                if (osSymbolId == "ogr-sym-4")
-                    VSIFPrintfL(fp, "s\n"); /* not filled */
-                else
-                    VSIFPrintfL(fp, "b*\n"); /* filled */
-            }
-            else if (osSymbolId == "ogr-sym-6" ||
-                     osSymbolId == "ogr-sym-7") /* triangle */
-            {
-                const double dfSqrt3 = 1.73205080757;
-                VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY - dfRadius * dfSqrt3 / 3);
-                VSIFPrintfL(fp, "%f %f l\n", dfX, dfY + 2 * dfRadius * dfSqrt3 / 3);
-                VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY - dfRadius * dfSqrt3 / 3);
-                if (osSymbolId == "ogr-sym-6")
-                    VSIFPrintfL(fp, "s\n"); /* not filled */
-                else
-                    VSIFPrintfL(fp, "b*\n"); /* filled */
-            }
-            else if (osSymbolId == "ogr-sym-8" ||
-                     osSymbolId == "ogr-sym-9") /* star */
+    if( !aosIncludedFields.empty() )
+    {
+        GDALPDFArrayRW* poArray = new GDALPDFArrayRW();
+        for( const auto& fieldName: aosIncludedFields )
+        {
+            int i = OGR_F_GetFieldIndex(hFeat, fieldName);
+            if (i >= 0 && OGR_F_IsFieldSetAndNotNull(hFeat, i))
             {
-                const double dfSin18divSin126 = 0.38196601125;
-                VSIFPrintfL(fp, "%f %f m\n", dfX, dfY + dfRadius);
-                for(int i=1; i<10;i++)
-                {
-                    double dfFactor = ((i % 2) == 1) ? dfSin18divSin126 : 1.0;
-                    VSIFPrintfL(fp, "%f %f l\n",
-                                dfX + cos(M_PI / 2 - i * M_PI * 36 / 180) * dfRadius * dfFactor,
-                                dfY + sin(M_PI / 2 - i * M_PI * 36 / 180) * dfRadius * dfFactor);
-                }
-                if (osSymbolId == "ogr-sym-8")
-                    VSIFPrintfL(fp, "s\n"); /* not filled */
+                OGRFieldDefnH hFDefn = OGR_F_GetFieldDefnRef( hFeat, i );
+                GDALPDFDictionaryRW* poKV = new GDALPDFDictionaryRW();
+                poKV->Add("N", OGR_Fld_GetNameRef(hFDefn));
+                if (OGR_Fld_GetType(hFDefn) == OFTInteger)
+                    poKV->Add("V", OGR_F_GetFieldAsInteger(hFeat, i));
+                else if (OGR_Fld_GetType(hFDefn) == OFTReal)
+                    poKV->Add("V", OGR_F_GetFieldAsDouble(hFeat, i));
                 else
-                    VSIFPrintfL(fp, "b*\n"); /* filled */
+                    poKV->Add("V", OGR_F_GetFieldAsString(hFeat, i));
+                poArray->Add(poKV);
             }
         }
-        else
-        {
-            DrawGeometry(fp, hGeom, adfMatrix);
-        }
 
-        VSIFPrintfL(fp, "Q");
+        poDictA->Add("P", poArray);
+    }
 
-        if (fpGZip)
-            VSIFCloseL(fpGZip);
-        fp = fpBack;
+    oDict.Add("K", nMCID);
+    oDict.Add("P", oParent, 0);
+    oDict.Add("Pg", oPage, 0);
+    oDict.Add("S", GDALPDFObjectRW::CreateName("feature"));
+    oDict.Add("T", osOutFeatureName);
 
-        vsi_l_offset nStreamEnd = VSIFTellL(fp);
-        VSIFPrintfL(fp, "\n");
-        VSIFPrintfL(fp, "endstream\n");
-        EndObj();
+    VSIFPrintfL(m_fp, "%s\n", oDict.Serialize().c_str());
 
-        StartObj(nObjectLengthId);
-        VSIFPrintfL(fp,
-                    "   %ld\n",
-                    (long)(nStreamEnd - nStreamStart));
-        EndObj();
+    EndObj();
+
+    return nFeatureUserProperties;
+}
+
+/************************************************************************/
+/*                            WriteLabel()                              */
+/************************************************************************/
+
+GDALPDFObjectNum GDALPDFBaseWriter::WriteLabel(OGRGeometryH hGeom,
+                                           const double adfMatrix[4],
+                                           ObjectStyle& os,
+                                           PDFCompressMethod eStreamCompressMethod,
+                                           double bboxXMin,
+                                           double bboxYMin,
+                                           double bboxXMax,
+                                           double bboxYMax)
+{
+    /* -------------------------------------------------------------- */
+    /*  Work out the text metrics for alignment purposes              */
+    /* -------------------------------------------------------------- */
+    double dfWidth, dfHeight;
+    CalculateText(os.osLabelText, os.osTextFont, os.dfTextSize,
+        os.bTextBold, os.bTextItalic, dfWidth, dfHeight);
+    dfWidth *= os.dfTextStretch;
+
+    if (os.nTextAnchor % 3 == 2) // horizontal center
+    {
+        os.dfTextDx -= (dfWidth / 2) * cos(os.dfTextAngle);
+        os.dfTextDy -= (dfWidth / 2) * sin(os.dfTextAngle);
     }
-    else
+    else if (os.nTextAnchor % 3 == 0) // right
+    {
+        os.dfTextDx -= dfWidth * cos(os.dfTextAngle);
+        os.dfTextDy -= dfWidth * sin(os.dfTextAngle);
+    }
+
+    if (os.nTextAnchor >= 4 && os.nTextAnchor <= 6) // vertical center
+    {
+        os.dfTextDx += (dfHeight / 2) * sin(os.dfTextAngle);
+        os.dfTextDy -= (dfHeight / 2) * cos(os.dfTextAngle);
+    }
+    else if (os.nTextAnchor >= 7 && os.nTextAnchor <= 9) // top
     {
-        osVectorDesc.aIds.push_back(0);
+        os.dfTextDx += dfHeight * sin(os.dfTextAngle);
+        os.dfTextDy -= dfHeight * cos(os.dfTextAngle);
     }
+    // modes 10,11,12 (baseline) unsupported for the time being
 
     /* -------------------------------------------------------------- */
-    /*  Write label                                                   */
+    /*  Write object dictionary                                       */
     /* -------------------------------------------------------------- */
-    if (!osLabelText.empty() && wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint)
+    auto nObjectId = AllocNewObject();
+    GDALPDFDictionaryRW oDict;
+
+    oDict.Add("Type", GDALPDFObjectRW::CreateName("XObject"))
+        .Add("BBox", &((new GDALPDFArrayRW())
+                        ->Add(bboxXMin).Add(bboxYMin)).Add(bboxXMax).Add(bboxYMax))
+        .Add("Subtype", GDALPDFObjectRW::CreateName("Form"));
+
+    GDALPDFDictionaryRW* poResources = new GDALPDFDictionaryRW();
+
+    if (os.nTextA != 255)
     {
-        if (osVectorDesc.nOCGTextId == 0)
-            osVectorDesc.nOCGTextId = WriteOCG("Text", osVectorDesc.nOGCId);
+        GDALPDFDictionaryRW* poGS1 = new GDALPDFDictionaryRW();
+        poGS1->Add("Type", GDALPDFObjectRW::CreateName("ExtGState"));
+        poGS1->Add("ca", (os.nTextA == 127 || os.nTextA == 128) ? 0.5 : os.nTextA / 255.0);
 
-        /* -------------------------------------------------------------- */
-        /*  Work out the text metrics for alignment purposes              */
-        /* -------------------------------------------------------------- */
-        double dfWidth, dfHeight;
-        CalculateText(osLabelText, osTextFont, dfTextSize,
-            bTextBold, bTextItalic, dfWidth, dfHeight);
-        dfWidth *= dfTextStretch;
+        GDALPDFDictionaryRW* poExtGState = new GDALPDFDictionaryRW();
+        poExtGState->Add("GS1", poGS1);
+
+        poResources->Add("ExtGState", poExtGState);
+    }
+
+    GDALPDFDictionaryRW* poDictF1 = new GDALPDFDictionaryRW();
+    poDictF1->Add("Type", GDALPDFObjectRW::CreateName("Font"));
+    poDictF1->Add("BaseFont", GDALPDFObjectRW::CreateName(os.osTextFont));
+    poDictF1->Add("Encoding", GDALPDFObjectRW::CreateName("WinAnsiEncoding"));
+    poDictF1->Add("Subtype", GDALPDFObjectRW::CreateName("Type1"));
+
+    GDALPDFDictionaryRW* poDictFont = new GDALPDFDictionaryRW();
+    poDictFont->Add("F1", poDictF1);
+    poResources->Add("Font", poDictFont);
+
+    oDict.Add("Resources", poResources);
+
+    StartObjWithStream(nObjectId, oDict,
+                        eStreamCompressMethod != COMPRESS_NONE);
+
+    /* -------------------------------------------------------------- */
+    /*  Write object stream                                           */
+    /* -------------------------------------------------------------- */
 
-        if (nTextAnchor % 3 == 2) // horizontal center
+    double dfX = OGR_G_GetX(hGeom, 0) * adfMatrix[1] + adfMatrix[0] + os.dfTextDx;
+    double dfY = OGR_G_GetY(hGeom, 0) * adfMatrix[3] + adfMatrix[2] + os.dfTextDy;
+
+    VSIFPrintfL(m_fp, "q\n");
+    VSIFPrintfL(m_fp, "BT\n");
+    if (os.nTextA != 255)
+    {
+        VSIFPrintfL(m_fp, "/GS1 gs\n");
+    }
+
+    VSIFPrintfL(m_fp, "%f %f %f %f %f %f Tm\n",
+                cos(os.dfTextAngle) * adfMatrix[1] * os.dfTextStretch,
+                sin(os.dfTextAngle) * adfMatrix[3] * os.dfTextStretch,
+                -sin(os.dfTextAngle) * adfMatrix[1],
+                cos(os.dfTextAngle) * adfMatrix[3],
+                dfX, dfY);
+
+    VSIFPrintfL(m_fp, "%f %f %f rg\n", os.nTextR / 255.0, os.nTextG / 255.0, os.nTextB / 255.0);
+    // The factor of adfMatrix[1] is introduced in the call to SetUnit near the top
+    // of this function. Because we are handling the 2D stretch correctly in Tm above,
+    // we don't need that factor here
+    VSIFPrintfL(m_fp, "/F1 %f Tf\n", os.dfTextSize / adfMatrix[1]);
+    VSIFPrintfL(m_fp, "(");
+    for(size_t i=0;i<os.osLabelText.size();i++)
+    {
+        if (os.osLabelText[i] == '(' || os.osLabelText[i] == ')' ||
+            os.osLabelText[i] == '\\')
         {
-            dfTextDx -= (dfWidth / 2) * cos(dfTextAngle);
-            dfTextDy -= (dfWidth / 2) * sin(dfTextAngle);
+            VSIFPrintfL(m_fp, "\\%c", os.osLabelText[i]);
         }
-        else if (nTextAnchor % 3 == 0) // right
+        else
         {
-            dfTextDx -= dfWidth * cos(dfTextAngle);
-            dfTextDy -= dfWidth * sin(dfTextAngle);
+            VSIFPrintfL(m_fp, "%c", os.osLabelText[i]);
         }
+    }
+    VSIFPrintfL(m_fp, ") Tj\n");
+    VSIFPrintfL(m_fp, "ET\n");
+    VSIFPrintfL(m_fp, "Q");
+
+    EndObjWithStream();
+    
+    return nObjectId;
+}
 
-        if (nTextAnchor >= 4 && nTextAnchor <= 6) // vertical center
+/************************************************************************/
+/*                          WriteOGRFeature()                           */
+/************************************************************************/
+
+int GDALPDFWriter::WriteOGRFeature(GDALPDFLayerDesc& osVectorDesc,
+                                   OGRFeatureH hFeat,
+                                   OGRCoordinateTransformationH hCT,
+                                   const char* pszOGRDisplayField,
+                                   const char* pszOGRLinkField,
+                                   int bWriteOGRAttributes,
+                                   int& iObj)
+{
+    GDALDataset* const  poClippingDS = oPageContext.poClippingDS;
+    const int  nHeight = poClippingDS->GetRasterYSize();
+    const double dfUserUnit = oPageContext.dfDPI * USER_UNIT_IN_INCH;
+    double adfGeoTransform[6];
+    poClippingDS->GetGeoTransform(adfGeoTransform);
+
+    double adfMatrix[4];
+    adfMatrix[0] = - adfGeoTransform[0] / (adfGeoTransform[1] * dfUserUnit) + oPageContext.sMargins.nLeft;
+    adfMatrix[1] = 1.0 / (adfGeoTransform[1] * dfUserUnit);
+    adfMatrix[2] = - (adfGeoTransform[3] + adfGeoTransform[5] * nHeight) / (-adfGeoTransform[5] * dfUserUnit) + oPageContext.sMargins.nBottom;
+    adfMatrix[3] = 1.0 / (-adfGeoTransform[5] * dfUserUnit);
+
+    OGRGeometryH hGeom = OGR_F_GetGeometryRef(hFeat);
+    if (hGeom == nullptr)
+    {
+        return TRUE;
+    }
+
+    OGREnvelope sEnvelope;
+
+    if( hCT != nullptr )
+    {
+        /* Reproject */
+        if( OGR_G_Transform(hGeom, hCT) != OGRERR_NONE )
         {
-            dfTextDx += (dfHeight / 2) * sin(dfTextAngle);
-            dfTextDy -= (dfHeight / 2) * cos(dfTextAngle);
+            return TRUE;
         }
-        else if (nTextAnchor >= 7 && nTextAnchor <= 9) // top
+
+        OGREnvelope sRasterEnvelope;
+        sRasterEnvelope.MinX = adfGeoTransform[0];
+        sRasterEnvelope.MinY = adfGeoTransform[3]
+            + poClippingDS->GetRasterYSize() * adfGeoTransform[5];
+        sRasterEnvelope.MaxX = adfGeoTransform[0]
+            + poClippingDS->GetRasterXSize() * adfGeoTransform[1];
+        sRasterEnvelope.MaxY = adfGeoTransform[3];
+
+        // Check that the reprojected geometry intersects the raster envelope.
+        OGR_G_GetEnvelope(hGeom, &sEnvelope);
+        if( !(sRasterEnvelope.Intersects(sEnvelope)) )
         {
-            dfTextDx += dfHeight * sin(dfTextAngle);
-            dfTextDy -= dfHeight * cos(dfTextAngle);
+            return TRUE;
         }
-        // modes 10,11,12 (baseline) unsupported for the time being
+    }
+    else
+    {
+        OGR_G_GetEnvelope(hGeom, &sEnvelope);
+    }
 
-        /* -------------------------------------------------------------- */
-        /*  Write object dictionary                                       */
-        /* -------------------------------------------------------------- */
-        int nObjectId = AllocNewObject();
-        int nObjectLengthId = AllocNewObject();
+    /* -------------------------------------------------------------- */
+    /*  Get style                                                     */
+    /* -------------------------------------------------------------- */
+    ObjectStyle os;
+    GetObjectStyle(nullptr, hFeat, adfMatrix, m_oMapSymbolFilenameToDesc, os);
 
-        osVectorDesc.aIdsText.push_back(nObjectId);
+    double dfRadius = os.dfSymbolSize * dfUserUnit;
 
-        StartObj(nObjectId);
-        {
-            GDALPDFDictionaryRW oDict;
+    // For a POINT with only a LABEL style string and non-empty text, we do not
+    // output any geometry other than the text itself.
+    const bool bLabelOnly = wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint &&
+        !os.bHasPenBrushOrSymbol && !os.osLabelText.empty();
 
-            int  nWidth = poClippingDS->GetRasterXSize();
-            double dfWidthInUserUnit = nWidth / dfUserUnit + oPageContext.sMargins.nLeft + oPageContext.sMargins.nRight;
-            double dfHeightInUserUnit = nHeight / dfUserUnit + oPageContext.sMargins.nBottom + oPageContext.sMargins.nTop;
+    /* -------------------------------------------------------------- */
+    /*  Write object dictionary                                       */
+    /* -------------------------------------------------------------- */
+    if (!bLabelOnly)
+    {
+        auto nObjectId = AllocNewObject();
 
-            oDict.Add("Length", nObjectLengthId, 0)
-                .Add("Type", GDALPDFObjectRW::CreateName("XObject"))
-                .Add("BBox", &((new GDALPDFArrayRW())
-                                ->Add(0).Add(0)).Add(dfWidthInUserUnit).Add(dfHeightInUserUnit))
-                .Add("Subtype", GDALPDFObjectRW::CreateName("Form"));
-            if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
-            {
-                oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode"));
-            }
+        osVectorDesc.aIds.push_back(nObjectId);
 
-            GDALPDFDictionaryRW* poResources = new GDALPDFDictionaryRW();
+        int bboxXMin, bboxYMin, bboxXMax, bboxYMax;
+        ComputeIntBBox(hGeom, sEnvelope, adfMatrix, os, dfRadius,
+                       bboxXMin, bboxYMin, bboxXMax, bboxYMax);
 
-            if (nTextA != 255)
-            {
-                GDALPDFDictionaryRW* poGS1 = new GDALPDFDictionaryRW();
-                poGS1->Add("Type", GDALPDFObjectRW::CreateName("ExtGState"));
-                poGS1->Add("ca", (nTextA == 127 || nTextA == 128) ? 0.5 : nTextA / 255.0);
+        auto nLinkId = WriteLink(hFeat, pszOGRLinkField, adfMatrix,
+                  bboxXMin, bboxYMin, bboxXMax, bboxYMax);
+        if( nLinkId.toBool() )
+            oPageContext.anAnnotationsId.push_back(nLinkId);
 
-                GDALPDFDictionaryRW* poExtGState = new GDALPDFDictionaryRW();
-                poExtGState->Add("GS1", poGS1);
+        GDALPDFDictionaryRW oDict;
+        GDALPDFArrayRW* poBBOX = new GDALPDFArrayRW();
+        poBBOX->Add(bboxXMin).Add(bboxYMin).Add(bboxXMax). Add(bboxYMax);
+        oDict.Add("Type", GDALPDFObjectRW::CreateName("XObject"))
+            .Add("BBox", poBBOX)
+            .Add("Subtype", GDALPDFObjectRW::CreateName("Form"));
 
-                poResources->Add("ExtGState", poExtGState);
-            }
+        GDALPDFDictionaryRW* poGS1 = new GDALPDFDictionaryRW();
+        poGS1->Add("Type", GDALPDFObjectRW::CreateName("ExtGState"));
+        if (os.nPenA != 255)
+            poGS1->Add("CA", (os.nPenA == 127 || os.nPenA == 128) ? 0.5 : os.nPenA / 255.0);
+        if (os.nBrushA != 255)
+            poGS1->Add("ca", (os.nBrushA == 127 || os.nBrushA == 128) ? 0.5 : os.nBrushA / 255.0 );
 
-            GDALPDFDictionaryRW* poDictF1 = new GDALPDFDictionaryRW();
-            poDictF1->Add("Type", GDALPDFObjectRW::CreateName("Font"));
-            poDictF1->Add("BaseFont", GDALPDFObjectRW::CreateName(osTextFont));
-            poDictF1->Add("Encoding", GDALPDFObjectRW::CreateName("WinAnsiEncoding"));
-            poDictF1->Add("Subtype", GDALPDFObjectRW::CreateName("Type1"));
+        GDALPDFDictionaryRW* poExtGState = new GDALPDFDictionaryRW();
+        poExtGState->Add("GS1", poGS1);
 
-            GDALPDFDictionaryRW* poDictFont = new GDALPDFDictionaryRW();
-            poDictFont->Add("F1", poDictF1);
-            poResources->Add("Font", poDictFont);
+        GDALPDFDictionaryRW* poResources = new GDALPDFDictionaryRW();
+        poResources->Add("ExtGState", poExtGState);
 
-            oDict.Add("Resources", poResources);
+        if( os.nImageSymbolId.toBool() )
+        {
+            GDALPDFDictionaryRW* poDictXObject = new GDALPDFDictionaryRW();
+            poResources->Add("XObject", poDictXObject);
 
-            VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
+            poDictXObject->Add(CPLSPrintf("SymImage%d", os.nImageSymbolId.toInt()), os.nImageSymbolId, 0);
         }
 
+        oDict.Add("Resources", poResources);
+
+        StartObjWithStream(nObjectId, oDict,
+                           oPageContext.eStreamCompressMethod != COMPRESS_NONE);
+
         /* -------------------------------------------------------------- */
         /*  Write object stream                                           */
         /* -------------------------------------------------------------- */
-        VSIFPrintfL(fp, "stream\n");
+        VSIFPrintfL(m_fp, "q\n");
 
-        vsi_l_offset nStreamStart = VSIFTellL(fp);
+        VSIFPrintfL(m_fp, "/GS1 gs\n");
 
-        VSILFILE* fpGZip = nullptr;
-        VSILFILE* fpBack = fp;
-        if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
-        {
-            fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE );
-            fp = fpGZip;
-        }
+        VSIFPrintfL(m_fp, "%s",
+                GenerateDrawingStream(hGeom, adfMatrix, os, dfRadius).c_str());
 
-        double dfX = OGR_G_GetX(hGeom, 0) * adfMatrix[1] + adfMatrix[0] + dfTextDx;
-        double dfY = OGR_G_GetY(hGeom, 0) * adfMatrix[3] + adfMatrix[2] + dfTextDy;
+        VSIFPrintfL(m_fp, "Q");
 
-        VSIFPrintfL(fp, "q\n");
-        VSIFPrintfL(fp, "BT\n");
-        if (nTextA != 255)
-        {
-            VSIFPrintfL(fp, "/GS1 gs\n");
-        }
-
-        VSIFPrintfL(fp, "%f %f %f %f %f %f Tm\n",
-                    cos(dfTextAngle) * adfMatrix[1] * dfTextStretch,
-                    sin(dfTextAngle) * adfMatrix[3] * dfTextStretch,
-                    -sin(dfTextAngle) * adfMatrix[1],
-                    cos(dfTextAngle) * adfMatrix[3],
-                    dfX, dfY);
-
-        VSIFPrintfL(fp, "%f %f %f rg\n", nTextR / 255.0, nTextG / 255.0, nTextB / 255.0);
-        // The factor of adfMatrix[1] is introduced in the call to SetUnit near the top
-        // of this function. Because we are handling the 2D stretch correctly in Tm above,
-        // we don't need that factor here
-        VSIFPrintfL(fp, "/F1 %f Tf\n", dfTextSize / adfMatrix[1]);
-        VSIFPrintfL(fp, "(");
-        for(size_t i=0;i<osLabelText.size();i++)
-        {
-            if (osLabelText[i] == '(' || osLabelText[i] == ')' ||
-                osLabelText[i] == '\\')
-            {
-                VSIFPrintfL(fp, "\\%c", osLabelText[i]);
-            }
-            else
-            {
-                VSIFPrintfL(fp, "%c", osLabelText[i]);
-            }
-        }
-        VSIFPrintfL(fp, ") Tj\n");
-        VSIFPrintfL(fp, "ET\n");
-        VSIFPrintfL(fp, "Q");
+        EndObjWithStream();
+    }
+    else
+    {
+        osVectorDesc.aIds.push_back(GDALPDFObjectNum());
+    }
 
-        if (fpGZip)
-            VSIFCloseL(fpGZip);
-        fp = fpBack;
+    /* -------------------------------------------------------------- */
+    /*  Write label                                                   */
+    /* -------------------------------------------------------------- */
+    if (!os.osLabelText.empty() && wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint)
+    {
+        if (!osVectorDesc.nOCGTextId.toBool())
+            osVectorDesc.nOCGTextId = WriteOCG("Text", osVectorDesc.nOCGId);
 
-        vsi_l_offset nStreamEnd = VSIFTellL(fp);
-        VSIFPrintfL(fp, "\n");
-        VSIFPrintfL(fp, "endstream\n");
-        EndObj();
+        int  nWidth = poClippingDS->GetRasterXSize();
+        double dfWidthInUserUnit = nWidth / dfUserUnit + oPageContext.sMargins.nLeft + oPageContext.sMargins.nRight;
+        double dfHeightInUserUnit = nHeight / dfUserUnit + oPageContext.sMargins.nBottom + oPageContext.sMargins.nTop;
+        auto nObjectId = WriteLabel(hGeom, adfMatrix, os,
+                                    oPageContext.eStreamCompressMethod,
+                                    0,0,dfWidthInUserUnit,dfHeightInUserUnit);
 
-        StartObj(nObjectLengthId);
-        VSIFPrintfL(fp,
-                    "   %ld\n",
-                    (long)(nStreamEnd - nStreamStart));
-        EndObj();
+        osVectorDesc.aIdsText.push_back(nObjectId);
     }
     else
     {
-        osVectorDesc.aIdsText.push_back(0);
+        osVectorDesc.aIdsText.push_back(GDALPDFObjectNum());
     }
 
     /* -------------------------------------------------------------- */
     /*  Write feature attributes                                      */
     /* -------------------------------------------------------------- */
-    int nFeatureUserProperties = 0;
+    GDALPDFObjectNum nFeatureUserProperties;
 
     CPLString osFeatureName;
 
     if (bWriteOGRAttributes)
     {
-        int iField = -1;
-        if (pszOGRDisplayField )
-            iField = OGR_FD_GetFieldIndex(OGR_F_GetDefnRef(hFeat), pszOGRDisplayField);
-        if( iField >= 0 )
-            osFeatureName = OGR_F_GetFieldAsString(hFeat, iField);
-        else
-            osFeatureName = CPLSPrintf("feature%d", iObjLayer + 1);
-
-        nFeatureUserProperties = AllocNewObject();
-        StartObj(nFeatureUserProperties);
-
-        GDALPDFDictionaryRW oDict;
-        GDALPDFDictionaryRW* poDictA = new GDALPDFDictionaryRW();
-        oDict.Add("A", poDictA);
-        poDictA->Add("O", GDALPDFObjectRW::CreateName("UserProperties"));
-
-        int nFields = OGR_F_GetFieldCount(hFeat);
-        GDALPDFArrayRW* poArray = new GDALPDFArrayRW();
-        for(int i = 0; i < nFields; i++)
-        {
-            if (OGR_F_IsFieldSetAndNotNull(hFeat, i))
-            {
-                OGRFieldDefnH hFDefn = OGR_F_GetFieldDefnRef( hFeat, i );
-                GDALPDFDictionaryRW* poKV = new GDALPDFDictionaryRW();
-                poKV->Add("N", OGR_Fld_GetNameRef(hFDefn));
-                if (OGR_Fld_GetType(hFDefn) == OFTInteger)
-                    poKV->Add("V", OGR_F_GetFieldAsInteger(hFeat, i));
-                else if (OGR_Fld_GetType(hFDefn) == OFTReal)
-                    poKV->Add("V", OGR_F_GetFieldAsDouble(hFeat, i));
-                else
-                    poKV->Add("V", OGR_F_GetFieldAsString(hFeat, i));
-                poArray->Add(poKV);
-            }
-        }
-
-        poDictA->Add("P", poArray);
-
-        oDict.Add("K", iObj);
-        oDict.Add("P", osVectorDesc.nFeatureLayerId, 0);
-        oDict.Add("Pg", oPageContext.nPageId, 0);
-        oDict.Add("S", GDALPDFObjectRW::CreateName("feature"));
-        oDict.Add("T", osFeatureName);
-
-        VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
-
-        EndObj();
+        nFeatureUserProperties = WriteAttributes(
+            hFeat,
+            osVectorDesc.aosIncludedFields,
+            pszOGRDisplayField, iObj,
+            osVectorDesc.nFeatureLayerId,
+            oPageContext.nPageId,
+            osFeatureName);
     }
 
     iObj ++;
-    iObjLayer ++;
 
     osVectorDesc.aUserPropertiesIds.push_back(nFeatureUserProperties);
     osVectorDesc.aFeatureNames.push_back(osFeatureName);
@@ -3195,11 +3369,11 @@
                            const char* pszOffLayers,
                            const char* pszExclusiveLayers)
 {
-    int nLayerExtraId = WriteOCG(pszExtraLayerName);
+    auto nLayerExtraId = WriteOCG(pszExtraLayerName);
     if( pszOffLayers )
-        osOffLayers = pszOffLayers;
+        m_osOffLayers = pszOffLayers;
     if( pszExclusiveLayers )
-        osExclusiveLayers = pszExclusiveLayers;
+        m_osExclusiveLayers = pszExclusiveLayers;
 
     /* -------------------------------------------------------------- */
     /*  Write extra images                                            */
@@ -3229,11 +3403,11 @@
             GDALDataset* poImageDS = (GDALDataset* )GDALOpen(pszImageFilename, GA_ReadOnly);
             if (poImageDS)
             {
-                int nImageId = WriteBlock( poImageDS,
+                auto nImageId = WriteBlock( poImageDS,
                                             0, 0,
                                             poImageDS->GetRasterXSize(),
                                             poImageDS->GetRasterYSize(),
-                                            0,
+                                            GDALPDFObjectNum(),
                                             COMPRESS_DEFAULT,
                                             0,
                                             -1,
@@ -3241,7 +3415,7 @@
                                             nullptr,
                                             nullptr );
 
-                if (nImageId)
+                if (nImageId.toBool())
                 {
                     GDALPDFImageDesc oImageDesc;
                     oImageDesc.nImageId = nImageId;
@@ -3254,7 +3428,7 @@
 
                     if( pszLinkVal != nullptr )
                     {
-                        int nAnnotId = AllocNewObject();
+                        auto nAnnotId = AllocNewObject();
                         oPageContext.anAnnotationsId.push_back(nAnnotId);
                         StartObj(nAnnotId);
                         {
@@ -3276,7 +3450,7 @@
                             oDict.Add("Border", &(new GDALPDFArrayRW())->Add(0).Add(0).Add(0));
                             oDict.Add("H", GDALPDFObjectRW::CreateName("I"));
 
-                            VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
+                            VSIFPrintfL(m_fp, "%s\n", oDict.Serialize().c_str());
                         }
                         EndObj();
                     }
@@ -3289,34 +3463,11 @@
     }
 
     /* -------------------------------------------------------------- */
-    /*  Write content dictionary                                      */
-    /* -------------------------------------------------------------- */
-    int nContentLengthId = AllocNewObject();
-
-    StartObj(oPageContext.nContentId);
-    {
-        GDALPDFDictionaryRW oDict;
-        oDict.Add("Length", nContentLengthId, 0);
-        if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
-        {
-            oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode"));
-        }
-        VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
-    }
-
-    /* -------------------------------------------------------------- */
     /*  Write content stream                                          */
     /* -------------------------------------------------------------- */
-    VSIFPrintfL(fp, "stream\n");
-    vsi_l_offset nStreamStart = VSIFTellL(fp);
-
-    VSILFILE* fpGZip = nullptr;
-    VSILFILE* fpBack = fp;
-    if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
-    {
-        fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE );
-        fp = fpGZip;
-    }
+    GDALPDFDictionaryRW oDictContent;
+    StartObjWithStream(oPageContext.nContentId, oDictContent,
+                       oPageContext.eStreamCompressMethod != COMPRESS_NONE );
 
     /* -------------------------------------------------------------- */
     /*  Write drawing instructions for raster blocks                  */
@@ -3324,17 +3475,17 @@
     for(size_t iRaster = 0; iRaster < oPageContext.asRasterDesc.size(); iRaster++)
     {
         const GDALPDFRasterDesc& oDesc = oPageContext.asRasterDesc[iRaster];
-        if (oDesc.nOCGRasterId)
-            VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", oDesc.nOCGRasterId);
+        if (oDesc.nOCGRasterId.toBool())
+            VSIFPrintfL(m_fp, "/OC /Lyr%d BDC\n", oDesc.nOCGRasterId.toInt());
 
         for(size_t iImage = 0; iImage < oDesc.asImageDesc.size(); iImage ++)
         {
-            VSIFPrintfL(fp, "q\n");
+            VSIFPrintfL(m_fp, "q\n");
             GDALPDFObjectRW* poXSize = GDALPDFObjectRW::CreateReal(oDesc.asImageDesc[iImage].dfXSize);
             GDALPDFObjectRW* poYSize = GDALPDFObjectRW::CreateReal(oDesc.asImageDesc[iImage].dfYSize);
             GDALPDFObjectRW* poXOff = GDALPDFObjectRW::CreateReal(oDesc.asImageDesc[iImage].dfXOff);
             GDALPDFObjectRW* poYOff = GDALPDFObjectRW::CreateReal(oDesc.asImageDesc[iImage].dfYOff);
-            VSIFPrintfL(fp, "%s 0 0 %s %s %s cm\n",
+            VSIFPrintfL(m_fp, "%s 0 0 %s %s %s cm\n",
                         poXSize->Serialize().c_str(),
                         poYSize->Serialize().c_str(),
                         poXOff->Serialize().c_str(),
@@ -3343,13 +3494,13 @@
             delete poYSize;
             delete poXOff;
             delete poYOff;
-            VSIFPrintfL(fp, "/Image%d Do\n",
-                        oDesc.asImageDesc[iImage].nImageId);
-            VSIFPrintfL(fp, "Q\n");
+            VSIFPrintfL(m_fp, "/Image%d Do\n",
+                        oDesc.asImageDesc[iImage].nImageId.toInt());
+            VSIFPrintfL(m_fp, "Q\n");
         }
 
-        if (oDesc.nOCGRasterId)
-            VSIFPrintfL(fp, "EMC\n");
+        if (oDesc.nOCGRasterId.toBool())
+            VSIFPrintfL(m_fp, "EMC\n");
     }
 
     /* -------------------------------------------------------------- */
@@ -3360,31 +3511,31 @@
     {
         GDALPDFLayerDesc& oLayerDesc = oPageContext.asVectorDesc[iLayer];
 
-        VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", oLayerDesc.nOGCId);
+        VSIFPrintfL(m_fp, "/OC /Lyr%d BDC\n", oLayerDesc.nOCGId.toInt());
 
         for(size_t iVector = 0; iVector < oLayerDesc.aIds.size(); iVector ++)
         {
-            if (oLayerDesc.aIds[iVector])
+            if (oLayerDesc.aIds[iVector].toBool())
             {
                 CPLString osName = oLayerDesc.aFeatureNames[iVector];
                 if (!osName.empty() )
                 {
-                    VSIFPrintfL(fp, "/feature <</MCID %d>> BDC\n",
+                    VSIFPrintfL(m_fp, "/feature <</MCID %d>> BDC\n",
                                 iObj);
                 }
 
-                VSIFPrintfL(fp, "/Vector%d Do\n", oLayerDesc.aIds[iVector]);
+                VSIFPrintfL(m_fp, "/Vector%d Do\n", oLayerDesc.aIds[iVector].toInt());
 
                 if (!osName.empty() )
                 {
-                    VSIFPrintfL(fp, "EMC\n");
+                    VSIFPrintfL(m_fp, "EMC\n");
                 }
             }
 
             iObj ++;
         }
 
-        VSIFPrintfL(fp, "EMC\n");
+        VSIFPrintfL(m_fp, "EMC\n");
     }
 
     /* -------------------------------------------------------------- */
@@ -3394,35 +3545,35 @@
     for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++)
     {
         GDALPDFLayerDesc& oLayerDesc = oPageContext.asVectorDesc[iLayer];
-        if (oLayerDesc.nOCGTextId)
+        if (oLayerDesc.nOCGTextId.toBool())
         {
-            VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", oLayerDesc.nOGCId);
-            VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", oLayerDesc.nOCGTextId);
+            VSIFPrintfL(m_fp, "/OC /Lyr%d BDC\n", oLayerDesc.nOCGId.toInt());
+            VSIFPrintfL(m_fp, "/OC /Lyr%d BDC\n", oLayerDesc.nOCGTextId.toInt());
 
             for(size_t iVector = 0; iVector < oLayerDesc.aIdsText.size(); iVector ++)
             {
-                if (oLayerDesc.aIdsText[iVector])
+                if (oLayerDesc.aIdsText[iVector].toBool())
                 {
                     CPLString osName = oLayerDesc.aFeatureNames[iVector];
                     if (!osName.empty() )
                     {
-                        VSIFPrintfL(fp, "/feature <</MCID %d>> BDC\n",
+                        VSIFPrintfL(m_fp, "/feature <</MCID %d>> BDC\n",
                                     iObj);
                     }
 
-                    VSIFPrintfL(fp, "/Text%d Do\n", oLayerDesc.aIdsText[iVector]);
+                    VSIFPrintfL(m_fp, "/Text%d Do\n", oLayerDesc.aIdsText[iVector].toInt());
 
                     if (!osName.empty() )
                     {
-                        VSIFPrintfL(fp, "EMC\n");
+                        VSIFPrintfL(m_fp, "EMC\n");
                     }
                 }
 
                 iObj ++;
             }
 
-            VSIFPrintfL(fp, "EMC\n");
-            VSIFPrintfL(fp, "EMC\n");
+            VSIFPrintfL(m_fp, "EMC\n");
+            VSIFPrintfL(m_fp, "EMC\n");
         }
         else
             iObj += (int) oLayerDesc.aIds.size();
@@ -3433,20 +3584,20 @@
     /* -------------------------------------------------------------- */
     if (pszExtraStream || !asExtraImageDesc.empty() )
     {
-        if (nLayerExtraId)
-            VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", nLayerExtraId);
+        if (nLayerExtraId.toBool())
+            VSIFPrintfL(m_fp, "/OC /Lyr%d BDC\n", nLayerExtraId.toInt());
 
         /* -------------------------------------------------------------- */
         /*  Write drawing instructions for extra images.                  */
         /* -------------------------------------------------------------- */
         for(size_t iImage = 0; iImage < asExtraImageDesc.size(); iImage ++)
         {
-            VSIFPrintfL(fp, "q\n");
+            VSIFPrintfL(m_fp, "q\n");
             GDALPDFObjectRW* poXSize = GDALPDFObjectRW::CreateReal(asExtraImageDesc[iImage].dfXSize);
             GDALPDFObjectRW* poYSize = GDALPDFObjectRW::CreateReal(asExtraImageDesc[iImage].dfYSize);
             GDALPDFObjectRW* poXOff = GDALPDFObjectRW::CreateReal(asExtraImageDesc[iImage].dfXOff);
             GDALPDFObjectRW* poYOff = GDALPDFObjectRW::CreateReal(asExtraImageDesc[iImage].dfYOff);
-            VSIFPrintfL(fp, "%s 0 0 %s %s %s cm\n",
+            VSIFPrintfL(m_fp, "%s 0 0 %s %s %s cm\n",
                         poXSize->Serialize().c_str(),
                         poYSize->Serialize().c_str(),
                         poXOff->Serialize().c_str(),
@@ -3455,68 +3606,54 @@
             delete poYSize;
             delete poXOff;
             delete poYOff;
-            VSIFPrintfL(fp, "/Image%d Do\n",
-                        asExtraImageDesc[iImage].nImageId);
-            VSIFPrintfL(fp, "Q\n");
+            VSIFPrintfL(m_fp, "/Image%d Do\n",
+                        asExtraImageDesc[iImage].nImageId.toInt());
+            VSIFPrintfL(m_fp, "Q\n");
         }
 
         if (pszExtraStream)
-            VSIFPrintfL(fp, "%s\n", pszExtraStream);
+            VSIFPrintfL(m_fp, "%s\n", pszExtraStream);
 
-        if (nLayerExtraId)
-            VSIFPrintfL(fp, "EMC\n");
+        if (nLayerExtraId.toBool())
+            VSIFPrintfL(m_fp, "EMC\n");
     }
 
-    if (fpGZip)
-        VSIFCloseL(fpGZip);
-    fp = fpBack;
-
-    vsi_l_offset nStreamEnd = VSIFTellL(fp);
-    if (fpGZip)
-        VSIFPrintfL(fp, "\n");
-    VSIFPrintfL(fp, "endstream\n");
-    EndObj();
-
-    StartObj(nContentLengthId);
-    VSIFPrintfL(fp,
-                "   %ld\n",
-                (long)(nStreamEnd - nStreamStart));
-    EndObj();
+    EndObjWithStream();
 
     /* -------------------------------------------------------------- */
     /*  Write objects for feature tree.                               */
     /* -------------------------------------------------------------- */
-    if (nStructTreeRootId)
+    if (m_nStructTreeRootId.toBool())
     {
-        int nParentTreeId = AllocNewObject();
+        auto nParentTreeId = AllocNewObject();
         StartObj(nParentTreeId);
-        VSIFPrintfL(fp, "<< /Nums [ 0 ");
-        VSIFPrintfL(fp, "[ ");
+        VSIFPrintfL(m_fp, "<< /Nums [ 0 ");
+        VSIFPrintfL(m_fp, "[ ");
         for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++)
         {
             GDALPDFLayerDesc& oLayerDesc = oPageContext.asVectorDesc[iLayer];
             for(size_t iVector = 0; iVector < oLayerDesc.aIds.size(); iVector ++)
             {
-                int nId = oLayerDesc.aUserPropertiesIds[iVector];
-                if (nId)
-                    VSIFPrintfL(fp, "%d 0 R ", nId);
+                const auto& nId = oLayerDesc.aUserPropertiesIds[iVector];
+                if (nId.toBool())
+                    VSIFPrintfL(m_fp, "%d 0 R ", nId.toInt());
             }
         }
-        VSIFPrintfL(fp, " ]\n");
-        VSIFPrintfL(fp, " ] >> \n");
+        VSIFPrintfL(m_fp, " ]\n");
+        VSIFPrintfL(m_fp, " ] >> \n");
         EndObj();
 
-        StartObj(nStructTreeRootId);
-        VSIFPrintfL(fp,
+        StartObj(m_nStructTreeRootId);
+        VSIFPrintfL(m_fp,
                     "<< "
                     "/Type /StructTreeRoot "
                     "/ParentTree %d 0 R "
-                    "/K [ ", nParentTreeId);
+                    "/K [ ", nParentTreeId.toInt());
         for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++)
         {
-            VSIFPrintfL(fp, "%d 0 R ", oPageContext.asVectorDesc[iLayer]. nFeatureLayerId);
+            VSIFPrintfL(m_fp, "%d 0 R ", oPageContext.asVectorDesc[iLayer]. nFeatureLayerId.toInt());
         }
-        VSIFPrintfL(fp,"] >>\n");
+        VSIFPrintfL(m_fp,"] >>\n");
         EndObj();
     }
 
@@ -3534,13 +3671,13 @@
             const GDALPDFRasterDesc& oDesc = oPageContext.asRasterDesc[iRaster];
             for(iImage = 0; iImage < oDesc.asImageDesc.size(); iImage ++)
             {
-                poDictXObject->Add(CPLSPrintf("Image%d", oDesc.asImageDesc[iImage].nImageId),
+                poDictXObject->Add(CPLSPrintf("Image%d", oDesc.asImageDesc[iImage].nImageId.toInt()),
                                 oDesc.asImageDesc[iImage].nImageId, 0);
             }
         }
         for(iImage = 0; iImage < asExtraImageDesc.size(); iImage ++)
         {
-            poDictXObject->Add(CPLSPrintf("Image%d", asExtraImageDesc[iImage].nImageId),
+            poDictXObject->Add(CPLSPrintf("Image%d", asExtraImageDesc[iImage].nImageId.toInt()),
                                asExtraImageDesc[iImage].nImageId, 0);
         }
         for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++)
@@ -3548,14 +3685,14 @@
             GDALPDFLayerDesc& oLayerDesc = oPageContext.asVectorDesc[iLayer];
             for(size_t iVector = 0; iVector < oLayerDesc.aIds.size(); iVector ++)
             {
-                if (oLayerDesc.aIds[iVector])
-                    poDictXObject->Add(CPLSPrintf("Vector%d", oLayerDesc.aIds[iVector]),
+                if (oLayerDesc.aIds[iVector].toBool())
+                    poDictXObject->Add(CPLSPrintf("Vector%d", oLayerDesc.aIds[iVector].toInt()),
                         oLayerDesc.aIds[iVector], 0);
             }
             for(size_t iVector = 0; iVector < oLayerDesc.aIdsText.size(); iVector ++)
             {
-                if (oLayerDesc.aIdsText[iVector])
-                    poDictXObject->Add(CPLSPrintf("Text%d", oLayerDesc.aIdsText[iVector]),
+                if (oLayerDesc.aIdsText[iVector].toBool())
+                    poDictXObject->Add(CPLSPrintf("Text%d", oLayerDesc.aIdsText[iVector].toInt()),
                         oLayerDesc.aIdsText[iVector], 0);
             }
         }
@@ -3616,16 +3753,16 @@
             }
         }
 
-        if (!asOCGs.empty() )
+        if (!m_asOCGs.empty() )
         {
             GDALPDFDictionaryRW* poDictProperties = new GDALPDFDictionaryRW();
-            for(size_t i=0; i<asOCGs.size(); i++)
-                poDictProperties->Add(CPLSPrintf("Lyr%d", asOCGs[i].nId),
-                                      asOCGs[i].nId, 0);
+            for(size_t i=0; i<m_asOCGs.size(); i++)
+                poDictProperties->Add(CPLSPrintf("Lyr%d", m_asOCGs[i].nId.toInt()),
+                                      m_asOCGs[i].nId, 0);
             oDict.Add("Properties", poDictProperties);
         }
 
-        VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
+        VSIFPrintfL(m_fp, "%s\n", oDict.Serialize().c_str());
     }
     EndObj();
 
@@ -3639,7 +3776,7 @@
         {
             oArray.Add(oPageContext.anAnnotationsId[i], 0);
         }
-        VSIFPrintfL(fp, "%s\n", oArray.Serialize().c_str());
+        VSIFPrintfL(m_fp, "%s\n", oArray.Serialize().c_str());
     }
     EndObj();
 
@@ -3650,14 +3787,14 @@
 /*                             WriteMask()                              */
 /************************************************************************/
 
-int GDALPDFWriter::WriteMask(GDALDataset* poSrcDS,
+GDALPDFObjectNum GDALPDFBaseWriter::WriteMask(GDALDataset* poSrcDS,
                              int nXOff, int nYOff, int nReqXSize, int nReqYSize,
                              PDFCompressMethod eCompressMethod)
 {
     int nMaskSize = nReqXSize * nReqYSize;
     GByte* pabyMask = (GByte*)VSIMalloc(nMaskSize);
     if (pabyMask == nullptr)
-        return 0;
+        return GDALPDFObjectNum();
 
     CPLErr eErr;
     eErr = poSrcDS->GetRasterBand(4)->RasterIO(
@@ -3669,7 +3806,7 @@
     if (eErr != CE_None)
     {
         VSIFree(pabyMask);
-        return 0;
+        return GDALPDFObjectNum();
     }
 
     int bOnly0or255 = TRUE;
@@ -3696,7 +3833,7 @@
     if (bOnly255)
     {
         CPLFree(pabyMask);
-        return 0;
+        return GDALPDFObjectNum();
     }
 
     if (bOnly0or255)
@@ -3707,7 +3844,7 @@
         if (pabyMask1 == nullptr)
         {
             CPLFree(pabyMask);
-            return 0;
+            return GDALPDFObjectNum();
         }
         for(int y=0;y<nReqYSize;y++)
         {
@@ -3722,52 +3859,22 @@
         nMaskSize = nReqXSize1 * nReqYSize;
     }
 
-    int nMaskId = AllocNewObject();
-    int nMaskLengthId = AllocNewObject();
+    auto nMaskId = AllocNewObject();
 
-    StartObj(nMaskId);
     GDALPDFDictionaryRW oDict;
-    oDict.Add("Length", nMaskLengthId, 0)
-         .Add("Type", GDALPDFObjectRW::CreateName("XObject"));
-    if( eCompressMethod != COMPRESS_NONE )
-    {
-        oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode"));
-    }
-    oDict.Add("Subtype", GDALPDFObjectRW::CreateName("Image"))
+    oDict.Add("Type", GDALPDFObjectRW::CreateName("XObject"))
+         .Add("Subtype", GDALPDFObjectRW::CreateName("Image"))
          .Add("Width", nReqXSize)
          .Add("Height", nReqYSize)
          .Add("ColorSpace", GDALPDFObjectRW::CreateName("DeviceGray"))
          .Add("BitsPerComponent", (bOnly0or255) ? 1 : 8);
-    VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
-    VSIFPrintfL(fp, "stream\n");
-    vsi_l_offset nStreamStart = VSIFTellL(fp);
 
-    VSILFILE* fpGZip = nullptr;
-    VSILFILE* fpBack = fp;
-    if( eCompressMethod != COMPRESS_NONE )
-    {
-        fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE );
-        fp = fpGZip;
-    }
+    StartObjWithStream(nMaskId, oDict, eCompressMethod != COMPRESS_NONE );
 
-    VSIFWriteL(pabyMask, nMaskSize, 1, fp);
+    VSIFWriteL(pabyMask, nMaskSize, 1, m_fp);
     CPLFree(pabyMask);
 
-    if (fpGZip)
-        VSIFCloseL(fpGZip);
-    fp = fpBack;
-
-    vsi_l_offset nStreamEnd = VSIFTellL(fp);
-    VSIFPrintfL(fp,
-                "\n"
-                "endstream\n");
-    EndObj();
-
-    StartObj(nMaskLengthId);
-    VSIFPrintfL(fp,
-                "   %ld\n",
-                (long)(nStreamEnd - nStreamStart));
-    EndObj();
+    EndObjWithStream();
 
     return nMaskId;
 }
@@ -3776,9 +3883,9 @@
 /*                             WriteBlock()                             */
 /************************************************************************/
 
-int GDALPDFWriter::WriteBlock(GDALDataset* poSrcDS,
+GDALPDFObjectNum GDALPDFBaseWriter::WriteBlock(GDALDataset* poSrcDS,
                              int nXOff, int nYOff, int nReqXSize, int nReqYSize,
-                             int nColorTableId,
+                             const GDALPDFObjectNum& nColorTableIdIn,
                              PDFCompressMethod eCompressMethod,
                              int nPredictor,
                              int nJPEGQuality,
@@ -3788,9 +3895,10 @@
 {
     int  nBands = poSrcDS->GetRasterCount();
     if (nBands == 0)
-        return 0;
+        return GDALPDFObjectNum();
 
-    if (nColorTableId == 0)
+    GDALPDFObjectNum nColorTableId(nColorTableIdIn);
+    if (!nColorTableId.toBool())
         nColorTableId = WriteColorTable(poSrcDS);
 
     CPLErr eErr = CE_None;
@@ -3828,7 +3936,7 @@
                 int nLength = (int)VSIFTellL(fpSrc);
                 VSIFSeekL(fpSrc, 0, SEEK_SET);
 
-                int nImageId = AllocNewObject();
+                auto nImageId = AllocNewObject();
 
                 StartObj(nImageId);
 
@@ -3843,14 +3951,14 @@
                         (nBands == 1) ?        GDALPDFObjectRW::CreateName("DeviceGray") :
                                                 GDALPDFObjectRW::CreateName("DeviceRGB"))
                      .Add("BitsPerComponent", 8);
-                VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
-                VSIFPrintfL(fp, "stream\n");
+                VSIFPrintfL(m_fp, "%s\n", oDict.Serialize().c_str());
+                VSIFPrintfL(m_fp, "stream\n");
 
                 GByte abyBuffer[1024];
                 for(int i=0;i<nLength;i += 1024)
                 {
                     int nRead = (int) VSIFReadL(abyBuffer, 1, 1024, fpSrc);
-                    if ((int)VSIFWriteL(abyBuffer, 1, nRead, fp) != nRead)
+                    if ((int)VSIFWriteL(abyBuffer, 1, nRead, m_fp) != nRead)
                     {
                         eErr = CE_Failure;
                         break;
@@ -3867,20 +3975,20 @@
                     }
                 }
 
-                VSIFPrintfL(fp, "\nendstream\n");
+                VSIFPrintfL(m_fp, "\nendstream\n");
 
                 EndObj();
 
                 VSIFCloseL(fpSrc);
 
-                return eErr == CE_None ? nImageId : 0;
+                return eErr == CE_None ? nImageId : GDALPDFObjectNum();
             }
         }
 
         eCompressMethod = COMPRESS_DEFLATE;
     }
 
-    int nMaskId = 0;
+    GDALPDFObjectNum nMaskId;
     if (nBands == 4)
     {
         nMaskId = WriteMask(poSrcDS,
@@ -3901,20 +4009,20 @@
 
         GDALDriverH hMemDriver = GDALGetDriverByName("MEM");
         if( hMemDriver == nullptr )
-            return 0;
+            return GDALPDFObjectNum();
 
         hMemDS = GDALCreate(hMemDriver, "MEM:::",
                             nReqXSize, nReqYSize, 0,
                             GDT_Byte, nullptr);
         if (hMemDS == nullptr)
-            return 0;
+            return GDALPDFObjectNum();
 
         pabyMEMDSBuffer =
             (GByte*)VSIMalloc3(nReqXSize, nReqYSize, nBands);
         if (pabyMEMDSBuffer == nullptr)
         {
             GDALClose(hMemDS);
-            return 0;
+            return GDALPDFObjectNum();
         }
 
         eErr = poSrcDS->RasterIO(GF_Read,
@@ -3928,7 +4036,7 @@
         {
             CPLFree(pabyMEMDSBuffer);
             GDALClose(hMemDS);
-            return 0;
+            return GDALPDFObjectNum();
         }
 
         int iBand;
@@ -3947,27 +4055,22 @@
         poBlockSrcDS = (GDALDataset*) hMemDS;
     }
 
-    int nImageId = AllocNewObject();
-    int nImageLengthId = AllocNewObject();
+    auto nImageId = AllocNewObject();
 
-    int nMeasureId = 0;
+    GDALPDFObjectNum nMeasureId;
     if( CPLTestBool(CPLGetConfigOption("GDAL_PDF_WRITE_GEOREF_ON_IMAGE", "FALSE")) &&
         nReqXSize == poSrcDS->GetRasterXSize() &&
         nReqYSize == poSrcDS->GetRasterYSize() )
     {
-        PDFMargins sMargins = {0, 0, 0, 0};
+        PDFMargins sMargins;
         nMeasureId = WriteSRS_ISO32000(poSrcDS, 1, nullptr, &sMargins, FALSE);
     }
 
-    StartObj(nImageId);
-
     GDALPDFDictionaryRW oDict;
-    oDict.Add("Length", nImageLengthId, 0)
-         .Add("Type", GDALPDFObjectRW::CreateName("XObject"));
+    oDict.Add("Type", GDALPDFObjectRW::CreateName("XObject"));
 
     if( eCompressMethod == COMPRESS_DEFLATE )
     {
-        oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode"));
         if( nPredictor == 2 )
             oDict.Add("DecodeParms", &((new GDALPDFDictionaryRW())
                                   ->Add("Predictor", 2)
@@ -3987,23 +4090,21 @@
          .Add("Width", nReqXSize)
          .Add("Height", nReqYSize)
          .Add("ColorSpace",
-              (nColorTableId != 0) ? GDALPDFObjectRW::CreateIndirect(nColorTableId, 0) :
+              (nColorTableId.toBool()) ? GDALPDFObjectRW::CreateIndirect(nColorTableId, 0) :
               (nBands == 1) ?        GDALPDFObjectRW::CreateName("DeviceGray") :
                                      GDALPDFObjectRW::CreateName("DeviceRGB"))
          .Add("BitsPerComponent", 8);
-    if( nMaskId )
+    if( nMaskId.toBool() )
     {
         oDict.Add("SMask", nMaskId, 0);
     }
-    if( nMeasureId )
+    if( nMeasureId.toBool() )
     {
         oDict.Add("Measure", nMeasureId, 0);
     }
 
-    VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
-    VSIFPrintfL(fp, "stream\n");
-
-    vsi_l_offset nStreamStart = VSIFTellL(fp);
+    StartObjWithStream(nImageId, oDict,
+                       eCompressMethod == COMPRESS_DEFLATE );
 
     if( eCompressMethod == COMPRESS_JPEG ||
         eCompressMethod == COMPRESS_JPEG2000 )
@@ -4084,19 +4185,11 @@
 
         vsi_l_offset nJPEGDataSize = 0;
         GByte* pabyJPEGData = VSIGetMemFileBuffer(szTmp, &nJPEGDataSize, TRUE);
-        VSIFWriteL(pabyJPEGData, static_cast<size_t>(nJPEGDataSize), 1, fp);
+        VSIFWriteL(pabyJPEGData, static_cast<size_t>(nJPEGDataSize), 1, m_fp);
         CPLFree(pabyJPEGData);
     }
     else
     {
-        VSILFILE* fpGZip = nullptr;
-        VSILFILE* fpBack = fp;
-        if( eCompressMethod == COMPRESS_DEFLATE )
-        {
-            fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE );
-            fp = fpGZip;
-        }
-
         GByte* pabyLine = (GByte*)CPLMalloc(nReqXSize * nBands);
         for(int iLine = 0; iLine < nReqYSize; iLine ++)
         {
@@ -4141,7 +4234,7 @@
                 }
             }
 
-            if( VSIFWriteL(pabyLine, nReqXSize * nBands, 1, fp) != 1 )
+            if( VSIFWriteL(pabyLine, nReqXSize * nBands, 1, m_fp) != 1 )
             {
                 eErr = CE_Failure;
                 break;
@@ -4159,10 +4252,6 @@
         }
 
         CPLFree(pabyLine);
-
-        if (fpGZip)
-            VSIFCloseL(fpGZip);
-        fp = fpBack;
     }
 
 end:
@@ -4174,67 +4263,31 @@
         hMemDS = nullptr;
     }
 
-    vsi_l_offset nStreamEnd = VSIFTellL(fp);
-    VSIFPrintfL(fp,
-                "\n"
-                "endstream\n");
-    EndObj();
-
-    StartObj(nImageLengthId);
-    VSIFPrintfL(fp,
-                "   %ld\n",
-                (long)(nStreamEnd - nStreamStart));
-    EndObj();
+    EndObjWithStream();
 
-    return eErr == CE_None ? nImageId : 0;
+    return eErr == CE_None ? nImageId : GDALPDFObjectNum();
 }
 
 /************************************************************************/
 /*                          WriteJavascript()                           */
 /************************************************************************/
 
-int GDALPDFWriter::WriteJavascript(const char* pszJavascript)
+GDALPDFObjectNum GDALPDFBaseWriter::WriteJavascript(const char* pszJavascript,
+                                                    bool bDeflate)
 {
-    int nJSId = AllocNewObject();
-    int nJSLengthId = AllocNewObject();
-    StartObj(nJSId);
+    auto nJSId = AllocNewObject();
     {
         GDALPDFDictionaryRW oDict;
-        oDict.Add("Length", nJSLengthId, 0);
-        if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
-        {
-            oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode"));
-        }
-        VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
-    }
-    VSIFPrintfL(fp, "stream\n");
-    vsi_l_offset nStreamStart = VSIFTellL(fp);
-
-    if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
-    {
-        VSILFILE* fpTemp = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE );
-        VSIFWriteL(pszJavascript, strlen(pszJavascript), 1, fpTemp);
-        VSIFCloseL(fpTemp);
-    }
-    else
-    {
-        VSIFWriteL(pszJavascript, strlen(pszJavascript), 1, fp);
-    }
+        StartObjWithStream(nJSId, oDict, bDeflate);
 
-    vsi_l_offset nStreamEnd = VSIFTellL(fp);
-    VSIFPrintfL(fp,
-                "\n"
-                "endstream\n");
-    EndObj();
+        VSIFWriteL(pszJavascript, strlen(pszJavascript), 1, m_fp);
+        VSIFPrintfL(m_fp, "\n");
 
-    StartObj(nJSLengthId);
-    VSIFPrintfL(fp,
-                "   %ld\n",
-                (long)(nStreamEnd - nStreamStart));
-    EndObj();
+        EndObjWithStream();
+    }
 
-    nNamesId = AllocNewObject();
-    StartObj(nNamesId);
+    m_nNamesId = AllocNewObject();
+    StartObj(m_nNamesId);
     {
         GDALPDFDictionaryRW oDict;
         GDALPDFDictionaryRW* poJavaScriptDict = new GDALPDFDictionaryRW();
@@ -4251,20 +4304,26 @@
         poJSDict->Add("JS", nJSId, 0);
         poJSDict->Add("S", GDALPDFObjectRW::CreateName("JavaScript"));
 
-        VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
+        VSIFPrintfL(m_fp, "%s\n", oDict.Serialize().c_str());
     }
     EndObj();
 
-    return nNamesId;
+    return m_nNamesId;
+}
+
+GDALPDFObjectNum GDALPDFWriter::WriteJavascript(const char* pszJavascript)
+{
+    return GDALPDFBaseWriter::WriteJavascript(pszJavascript,
+                           oPageContext.eStreamCompressMethod != COMPRESS_NONE);
 }
 
 /************************************************************************/
 /*                        WriteJavascriptFile()                         */
 /************************************************************************/
 
-int GDALPDFWriter::WriteJavascriptFile(const char* pszJavascriptFile)
+GDALPDFObjectNum GDALPDFWriter::WriteJavascriptFile(const char* pszJavascriptFile)
 {
-    int nRet = 0;
+    GDALPDFObjectNum nId;
     char* pszJavascriptToFree = (char*)CPLMalloc(65536);
     VSILFILE* fpJS = VSIFOpenL(pszJavascriptFile, "rb");
     if( fpJS != nullptr )
@@ -4273,42 +4332,43 @@
         if( nRead < 65536 )
         {
             pszJavascriptToFree[nRead] = '\0';
-            nRet = WriteJavascript(pszJavascriptToFree);
+            nId = WriteJavascript(pszJavascriptToFree);
         }
         VSIFCloseL(fpJS);
     }
     CPLFree(pszJavascriptToFree);
-    return nRet;
+    return nId;
 }
+
 /************************************************************************/
 /*                              WritePages()                            */
 /************************************************************************/
 
 void GDALPDFWriter::WritePages()
 {
-    StartObj(nPageResourceId);
+    StartObj(m_nPageResourceId);
     {
         GDALPDFDictionaryRW oDict;
         GDALPDFArrayRW* poKids = new GDALPDFArrayRW();
         oDict.Add("Type", GDALPDFObjectRW::CreateName("Pages"))
-             .Add("Count", (int)asPageId.size())
+             .Add("Count", (int)m_asPageId.size())
              .Add("Kids", poKids);
 
-        for(size_t i=0;i<asPageId.size();i++)
-            poKids->Add(asPageId[i], 0);
+        for(size_t i=0;i<m_asPageId.size();i++)
+            poKids->Add(m_asPageId[i], 0);
 
-        VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
+        VSIFPrintfL(m_fp, "%s\n", oDict.Serialize().c_str());
     }
     EndObj();
 
-    StartObj(nCatalogId);
+    StartObj(m_nCatalogId);
     {
         GDALPDFDictionaryRW oDict;
         oDict.Add("Type", GDALPDFObjectRW::CreateName("Catalog"))
-             .Add("Pages", nPageResourceId, 0);
-        if (nXMPId)
-            oDict.Add("Metadata", nXMPId, 0);
-        if (!asOCGs.empty() )
+             .Add("Pages", m_nPageResourceId, 0);
+        if (m_nXMPId.toBool())
+            oDict.Add("Metadata", m_nXMPId, 0);
+        if (!m_asOCGs.empty() )
         {
             GDALPDFDictionaryRW* poDictOCProperties = new GDALPDFDictionaryRW();
             oDict.Add("OCProperties", poDictOCProperties);
@@ -4318,13 +4378,13 @@
 
             /* Build "Order" array of D dict */
             GDALPDFArrayRW* poArrayOrder = new GDALPDFArrayRW();
-            for(size_t i=0;i<asOCGs.size();i++)
+            for(size_t i=0;i<m_asOCGs.size();i++)
             {
-                poArrayOrder->Add(asOCGs[i].nId, 0);
-                if (i + 1 < asOCGs.size() && asOCGs[i+1].nParentId == asOCGs[i].nId)
+                poArrayOrder->Add(m_asOCGs[i].nId, 0);
+                if (i + 1 < m_asOCGs.size() && m_asOCGs[i+1].nParentId == m_asOCGs[i].nId)
                 {
                     GDALPDFArrayRW* poSubArrayOrder = new GDALPDFArrayRW();
-                    poSubArrayOrder->Add(asOCGs[i+1].nId, 0);
+                    poSubArrayOrder->Add(m_asOCGs[i+1].nId, 0);
                     poArrayOrder->Add(poSubArrayOrder);
                     i ++;
                 }
@@ -4332,22 +4392,22 @@
             poDictD->Add("Order", poArrayOrder);
 
             /* Build "OFF" array of D dict */
-            if( !osOffLayers.empty() )
+            if( !m_osOffLayers.empty() )
             {
                 GDALPDFArrayRW* poArrayOFF = new GDALPDFArrayRW();
-                char** papszTokens = CSLTokenizeString2(osOffLayers, ",", 0);
+                char** papszTokens = CSLTokenizeString2(m_osOffLayers, ",", 0);
                 for(int i=0; papszTokens[i] != nullptr; i++)
                 {
                     size_t j;
                     int bFound = FALSE;
-                    for(j=0;j<asOCGs.size();j++)
+                    for(j=0;j<m_asOCGs.size();j++)
                     {
-                        if( strcmp(papszTokens[i], asOCGs[j].osLayerName) == 0)
+                        if( strcmp(papszTokens[i], m_asOCGs[j].osLayerName) == 0)
                         {
-                            poArrayOFF->Add(asOCGs[j].nId, 0);
+                            poArrayOFF->Add(m_asOCGs[j].nId, 0);
                             bFound = TRUE;
                         }
-                        if (j + 1 < asOCGs.size() && asOCGs[j+1].nParentId == asOCGs[j].nId)
+                        if (j + 1 < m_asOCGs.size() && m_asOCGs[j+1].nParentId == m_asOCGs[j].nId)
                         {
                             j ++;
                         }
@@ -4365,22 +4425,22 @@
             }
 
             /* Build "RBGroups" array of D dict */
-            if( !osExclusiveLayers.empty() )
+            if( !m_osExclusiveLayers.empty() )
             {
                 GDALPDFArrayRW* poArrayRBGroups = new GDALPDFArrayRW();
-                char** papszTokens = CSLTokenizeString2(osExclusiveLayers, ",", 0);
+                char** papszTokens = CSLTokenizeString2(m_osExclusiveLayers, ",", 0);
                 for(int i=0; papszTokens[i] != nullptr; i++)
                 {
                     size_t j;
                     int bFound = FALSE;
-                    for(j=0;j<asOCGs.size();j++)
+                    for(j=0;j<m_asOCGs.size();j++)
                     {
-                        if( strcmp(papszTokens[i], asOCGs[j].osLayerName) == 0)
+                        if( strcmp(papszTokens[i], m_asOCGs[j].osLayerName) == 0)
                         {
-                            poArrayRBGroups->Add(asOCGs[j].nId, 0);
+                            poArrayRBGroups->Add(m_asOCGs[j].nId, 0);
                             bFound = TRUE;
                         }
-                        if (j + 1 < asOCGs.size() && asOCGs[j+1].nParentId == asOCGs[j].nId)
+                        if (j + 1 < m_asOCGs.size() && m_asOCGs[j+1].nParentId == m_asOCGs[j].nId)
                         {
                             j ++;
                         }
@@ -4405,24 +4465,24 @@
             }
 
             GDALPDFArrayRW* poArrayOGCs = new GDALPDFArrayRW();
-            for(size_t i=0;i<asOCGs.size();i++)
-                poArrayOGCs->Add(asOCGs[i].nId, 0);
+            for(size_t i=0;i<m_asOCGs.size();i++)
+                poArrayOGCs->Add(m_asOCGs[i].nId, 0);
             poDictOCProperties->Add("OCGs", poArrayOGCs);
         }
 
-        if (nStructTreeRootId)
+        if (m_nStructTreeRootId.toBool())
         {
             GDALPDFDictionaryRW* poDictMarkInfo = new GDALPDFDictionaryRW();
             oDict.Add("MarkInfo", poDictMarkInfo);
             poDictMarkInfo->Add("UserProperties", GDALPDFObjectRW::CreateBool(TRUE));
 
-            oDict.Add("StructTreeRoot", nStructTreeRootId, 0);
+            oDict.Add("StructTreeRoot", m_nStructTreeRootId, 0);
         }
 
-        if (nNamesId)
-            oDict.Add("Names", nNamesId, 0);
+        if (m_nNamesId.toBool())
+            oDict.Add("Names", m_nNamesId, 0);
 
-        VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
+        VSIFPrintfL(m_fp, "%s\n", oDict.Serialize().c_str());
     }
     EndObj();
 }
@@ -4453,7 +4513,7 @@
 /*                         GDALPDFClippingDataset                       */
 /************************************************************************/
 
-class GDALPDFClippingDataset: public GDALDataset
+class GDALPDFClippingDataset final: public GDALDataset
 {
         GDALDataset* poSrcDS;
         double adfGeoTransform[6];
@@ -4483,6 +4543,21 @@
         {
             return poSrcDS->GetProjectionRef();
         }
+
+        virtual OGRSpatialReference* GetSpatialRef() 
+        {
+            const char* pWKT = poSrcDS->GetProjectionRef();
+            if( !pWKT || pWKT[0] == '\0')
+            {
+                return nullptr;
+            }
+            OGRSpatialReference *m_pSRS = new OGRSpatialReference();
+            if( m_pSRS->importFromWkt(pWKT) != OGRERR_NONE )
+            {
+                return nullptr;
+            }
+            return m_pSRS;
+        }
 };
 
 /************************************************************************/
@@ -4594,7 +4669,7 @@
     if( pszValue != nullptr )
     {
         nBlockXSize = atoi( pszValue );
-        if (nBlockXSize < 0 || nBlockXSize >= nWidth)
+        if (nBlockXSize <= 0 || nBlockXSize >= nWidth)
             nBlockXSize = nWidth;
     }
 
@@ -4602,7 +4677,7 @@
     if( pszValue != nullptr )
     {
         nBlockYSize = atoi( pszValue );
-        if (nBlockYSize < 0 || nBlockYSize >= nHeight)
+        if (nBlockYSize <= 0 || nBlockYSize >= nHeight)
             nBlockYSize = nHeight;
     }
 
@@ -4983,7 +5058,7 @@
     }
     else
     {
-#if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) || defined(HAVE_PDFIUM)
+#ifdef HAVE_PDF_READ_SUPPORT
         GDALDataset* poDS = GDALPDFOpen(pszFilename, GA_ReadOnly);
         if( poDS == nullptr )
             return nullptr;
diff -urN gdal-2.3.2/frmts/pdf/pdfcreatecopy.h gdal-2.3.2-pdf/frmts/pdf/pdfcreatecopy.h
--- gdal-2.3.2/frmts/pdf/pdfcreatecopy.h	2018-09-21 18:04:33.000000000 +0900
+++ gdal-2.3.2-pdf/frmts/pdf/pdfcreatecopy.h	2019-12-12 12:44:04.921943906 +0900
@@ -1,12 +1,12 @@
 /******************************************************************************
- * $Id: pdfcreatecopy.h e13dcd4dc171dfeed63f912ba06b9374ce4f3bb2 2018-03-18 21:37:41Z Even Rouault $
+ * $Id$
  *
  * Project:  PDF driver
  * Purpose:  GDALDataset driver for PDF dataset.
- * Author:   Even Rouault, <even dot rouault at mines dash paris dot org>
+ * Author:   Even Rouault, <even dot rouault at spatialys dot com>
  *
  ******************************************************************************
- * Copyright (c) 2012-2013, Even Rouault <even dot rouault at mines-paris dot org>
+ * Copyright (c) 2012-2019, Even Rouault <even dot rouault at spatialys dot com>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -36,6 +36,13 @@
 #include <map>
 
 #include "ogr_api.h"
+#include "ogr_spatialref.h"
+
+/* Cf PDF reference v1.7, Appendix C, page 993 */
+#define MAXIMUM_SIZE_IN_UNITS   14400
+
+#define APPLY_GT_X(gt, x, y) ((gt)[0] + (x) * (gt)[1] + (y) * (gt)[2])
+#define APPLY_GT_Y(gt, x, y) ((gt)[3] + (x) * (gt)[4] + (y) * (gt)[5])
 
 typedef enum
 {
@@ -46,13 +53,19 @@
     COMPRESS_DEFAULT
 } PDFCompressMethod;
 
-typedef struct
+struct PDFMargins
+{
+    int nLeft = 0;
+    int nRight = 0;
+    int nTop = 0;
+    int nBottom = 0;
+};
+
+class GDALFakePDFDataset final: public GDALDataset
 {
-    int nLeft;
-    int nRight;
-    int nTop;
-    int nBottom;
-} PDFMargins;
+    public:
+        GDALFakePDFDataset() = default;
+};
 
 /************************************************************************/
 /*                          GDALPDFWriter                               */
@@ -61,12 +74,12 @@
 class GDALXRefEntry
 {
     public:
-        vsi_l_offset    nOffset;
-        int             nGen;
-        int             bFree;
+        vsi_l_offset    nOffset = 0;
+        int             nGen = 0;
+        int             bFree = FALSE;
 
-        GDALXRefEntry() : nOffset(0), nGen(0), bFree(FALSE) {}
-        GDALXRefEntry(vsi_l_offset nOffsetIn, int nGenIn = 0) : nOffset(nOffsetIn), nGen(nGenIn), bFree(FALSE) {}
+        GDALXRefEntry() = default;
+        explicit GDALXRefEntry(vsi_l_offset nOffsetIn, int nGenIn = 0) : nOffset(nOffsetIn), nGen(nGenIn) {}
         GDALXRefEntry(const GDALXRefEntry& oOther) : nOffset(oOther.nOffset), nGen(oOther.nGen), bFree(oOther.bFree) {}
         GDALXRefEntry& operator= (const GDALXRefEntry& oOther) { nOffset = oOther.nOffset; nGen = oOther.nGen; bFree = oOther.bFree; return *this; }
 };
@@ -74,152 +87,275 @@
 class GDALPDFImageDesc
 {
     public:
-        int          nImageId;
-        double       dfXOff;
-        double       dfYOff;
-        double       dfXSize;
-        double       dfYSize;
+        GDALPDFObjectNum  nImageId{};
+        double       dfXOff = 0;
+        double       dfYOff = 0;
+        double       dfXSize = 0;
+        double       dfYSize = 0;
 };
 
 class GDALPDFLayerDesc
 {
     public:
-        int          nOGCId;
-        int          nOCGTextId;
-        int          nFeatureLayerId;
-        CPLString    osLayerName;
-        int          bWriteOGRAttributes;
-        std::vector<int> aIds;
-        std::vector<int> aIdsText;
-        std::vector<int> aUserPropertiesIds;
-        std::vector<CPLString> aFeatureNames;
+        GDALPDFObjectNum  nOCGId{};
+        GDALPDFObjectNum  nOCGTextId{};
+        GDALPDFObjectNum  nFeatureLayerId{};
+        CPLString    osLayerName{};
+        int          bWriteOGRAttributes{false};
+        std::vector<GDALPDFObjectNum> aIds{};
+        std::vector<GDALPDFObjectNum> aIdsText{};
+        std::vector<GDALPDFObjectNum> aUserPropertiesIds{};
+        std::vector<CPLString> aFeatureNames{};
+        std::vector<CPLString> aosIncludedFields{};
 };
 
 class GDALPDFRasterDesc
 {
     public:
-        int          nOCGRasterId;
-        std::vector<GDALPDFImageDesc> asImageDesc;
+        GDALPDFObjectNum nOCGRasterId{};
+        std::vector<GDALPDFImageDesc> asImageDesc{};
 };
 
 class GDALPDFPageContext
 {
     public:
-        GDALDataset* poClippingDS;
-        PDFCompressMethod eStreamCompressMethod;
-        double       dfDPI;
-        PDFMargins   sMargins;
-        int          nPageId;
-        int          nContentId;
-        int          nResourcesId;
-        std::vector<GDALPDFLayerDesc> asVectorDesc;
-        std::vector<GDALPDFRasterDesc> asRasterDesc;
-        int          nAnnotsId;
-        std::vector<int> anAnnotationsId;
-
-        GDALPDFPageContext() :
-            poClippingDS( nullptr ),
-            eStreamCompressMethod( COMPRESS_NONE ),
-            dfDPI( 0.0 ),
-            nPageId( 0 ),
-            nContentId( 0 ),
-            nResourcesId( 0 ),
-            nAnnotsId( 0 )
-        {
-            sMargins.nLeft = 0;
-            sMargins.nRight = 0;
-            sMargins.nTop = 0;
-            sMargins.nBottom = 0;
-        }
+        GDALDataset* poClippingDS = nullptr;
+        PDFCompressMethod eStreamCompressMethod = COMPRESS_NONE;
+        double       dfDPI{0};
+        PDFMargins   sMargins{};
+        GDALPDFObjectNum  nPageId{};
+        GDALPDFObjectNum  nContentId{};
+        GDALPDFObjectNum  nResourcesId{};
+        std::vector<GDALPDFLayerDesc> asVectorDesc{};
+        std::vector<GDALPDFRasterDesc> asRasterDesc{};
+        GDALPDFObjectNum  nAnnotsId{};
+        std::vector<GDALPDFObjectNum> anAnnotationsId{};
 };
 
 class GDALPDFOCGDesc
 {
     public:
-        int          nId;
-        int          nParentId;
-        CPLString    osLayerName;
-};
-
-class GDALPDFWriter
-{
-    VSILFILE* fp;
-    std::vector<GDALXRefEntry> asXRefEntries;
-    std::vector<int> asPageId;
-    std::vector<GDALPDFOCGDesc> asOCGs;
-    std::map<CPLString,GDALPDFImageDesc> oMapSymbolFilenameToDesc;
-
-    int nInfoId;
-    int nInfoGen;
-    int nPageResourceId;
-    int nStructTreeRootId;
-    int nCatalogId;
-    int nCatalogGen;
-    int nXMPId;
-    int nXMPGen;
-    int nNamesId;
-    int bInWriteObj;
-
-    vsi_l_offset nLastStartXRef;
-    int nLastXRefSize;
-    int bCanUpdate;
+        GDALPDFObjectNum  nId{};
+        GDALPDFObjectNum  nParentId{};
+        CPLString    osLayerName{};
+};
 
-    GDALPDFPageContext oPageContext;
+class GDALPDFBaseWriter
+{
+protected:
+    VSILFILE* m_fp = nullptr;
+    bool m_bInWriteObj = false;
+    std::vector<GDALXRefEntry> m_asXRefEntries{};
+    GDALPDFObjectNum m_nPageResourceId{};
+    GDALPDFObjectNum m_nCatalogId{};
+    int         m_nCatalogGen = 0;
+    GDALPDFObjectNum m_nInfoId{};
+    int         m_nInfoGen = 0;
+    GDALPDFObjectNum m_nXMPId{};
+    int         m_nXMPGen = 0;
+    GDALPDFObjectNum m_nStructTreeRootId{};
+    GDALPDFObjectNum m_nNamesId{};
+
+    GDALPDFObjectNum m_nContentLengthId{};
+    VSILFILE* m_fpBack = nullptr;
+    VSILFILE* m_fpGZip = nullptr;
+    vsi_l_offset m_nStreamStart = 0;
+
+    std::vector<GDALPDFObjectNum> m_asPageId{};
+    std::vector<GDALPDFOCGDesc> m_asOCGs{};
+    std::map<CPLString,GDALPDFImageDesc> m_oMapSymbolFilenameToDesc{};
+
+public:
+
+    struct ObjectStyle
+    {
+        unsigned int nPenR = 0;
+        unsigned int nPenG = 0;
+        unsigned int nPenB = 0;
+        unsigned int nPenA = 255;
+        unsigned int nBrushR = 127;
+        unsigned int nBrushG = 127;
+        unsigned int nBrushB = 127;
+        unsigned int nBrushA = 127;
+        unsigned int nTextR = 0;
+        unsigned int nTextG = 0;
+        unsigned int nTextB = 0;
+        unsigned int nTextA = 255;
+        int bSymbolColorDefined = FALSE;
+        unsigned int nSymbolR = 0;
+        unsigned int nSymbolG = 0;
+        unsigned int nSymbolB = 0;
+        unsigned int nSymbolA = 255;
+        bool bHasPenBrushOrSymbol = false;
+        CPLString osTextFont{};
+        bool bTextBold = false;
+        bool bTextItalic = false;
+        double dfTextSize = 12.0;
+        double dfTextAngle = 0.0;
+        double dfTextStretch = 1.0;
+        double dfTextDx = 0.0;
+        double dfTextDy = 0.0;
+        int nTextAnchor = 1;
+        double dfPenWidth = 1.0;
+        double dfSymbolSize = 5.0;
+        CPLString osDashArray{};
+        CPLString osLabelText{};
+        CPLString osSymbolId{};
+        GDALPDFObjectNum nImageSymbolId{};
+        int nImageWidth = 0;
+        int nImageHeight = 0;
+    };
+
+protected:
+    explicit GDALPDFBaseWriter(VSILFILE* fp);
+    ~GDALPDFBaseWriter();
 
-    CPLString    osOffLayers;
-    CPLString    osExclusiveLayers;
+    GDALPDFObjectNum AllocNewObject();
 
-    void    StartObj(int nObjectId, int nGen = 0);
+    void    StartObj(const GDALPDFObjectNum& nObjectId, int nGen = 0);
     void    EndObj();
-    void    WriteXRefTableAndTrailer();
-    void    WritePages();
-    int     WriteBlock( GDALDataset* poSrcDS,
+
+    void    StartObjWithStream(const GDALPDFObjectNum& nObjectId,
+                                           GDALPDFDictionaryRW& oDict,
+                                           bool bDeflate);
+    void    EndObjWithStream();
+
+    void    StartNewDoc();
+    void    Close();
+
+    void    WriteXRefTableAndTrailer(bool bUpdate,
+                                     vsi_l_offset nLastStartXRef);
+
+    GDALPDFObjectNum     WriteSRS_ISO32000(GDALDataset* poSrcDS,
+                                double dfUserUnit,
+                                const char* pszNEATLINE,
+                                PDFMargins* psMargins,
+                                int bWriteViewport);
+    GDALPDFObjectNum     WriteSRS_OGC_BP(GDALDataset* poSrcDS,
+                            double dfUserUnit,
+                            const char* pszNEATLINE,
+                            PDFMargins* psMargins);
+    static GDALPDFDictionaryRW* GDALPDFBuildOGC_BP_Projection(const OGRSpatialReference* poSRS);
+
+    GDALPDFObjectNum WriteOCG(const char* pszLayerName, const GDALPDFObjectNum& nParentId = GDALPDFObjectNum());
+
+    GDALPDFObjectNum     WriteBlock( GDALDataset* poSrcDS,
                         int nXOff, int nYOff, int nReqXSize, int nReqYSize,
-                        int nColorTableId,
+                        const GDALPDFObjectNum& nColorTableIdIn,
                         PDFCompressMethod eCompressMethod,
                         int nPredictor,
                         int nJPEGQuality,
                         const char* pszJPEG2000_DRIVER,
                         GDALProgressFunc pfnProgress,
                         void * pProgressData );
-    int     WriteMask(GDALDataset* poSrcDS,
+    GDALPDFObjectNum     WriteMask(GDALDataset* poSrcDS,
                       int nXOff, int nYOff, int nReqXSize, int nReqYSize,
                       PDFCompressMethod eCompressMethod);
-    int     WriteOCG(const char* pszLayerName, int nParentId = 0);
 
-    int     WriteColorTable(GDALDataset* poSrcDS);
+    GDALPDFObjectNum     WriteColorTable(GDALDataset* poSrcDS);
 
-    int     AllocNewObject();
+    void GetObjectStyle(const char* pszStyleString,
+                        OGRFeatureH hFeat, const double adfMatrix[4],
+                        std::map<CPLString,GDALPDFImageDesc> oMapSymbolFilenameToDesc,
+                        ObjectStyle& os);
+    static CPLString GenerateDrawingStream(OGRGeometryH hGeom,
+                                    const double adfMatrix[4],
+                                    ObjectStyle& os,
+                                    double dfRadius);
+    GDALPDFObjectNum WriteAttributes(
+        OGRFeatureH hFeat,
+        const std::vector<CPLString>& aosIncludedFields,
+        const char* pszOGRDisplayField,
+        int nMCID,
+        const GDALPDFObjectNum& oParent,
+        const GDALPDFObjectNum& oPage,
+        CPLString& osOutFeatureName);
+
+    GDALPDFObjectNum WriteLabel(OGRGeometryH hGeom,
+                                    const double adfMatrix[4],
+                                    ObjectStyle& os,
+                                    PDFCompressMethod eStreamCompressMethod,
+                                    double bboxXMin,
+                                    double bboxYMin,
+                                    double bboxXMax,
+                                    double bboxYMax);
 
-    public:
-        GDALPDFWriter( VSILFILE* fpIn, int bAppend = FALSE );
-       ~GDALPDFWriter();
+    GDALPDFObjectNum WriteLink(OGRFeatureH hFeat,
+                              const char* pszOGRLinkField,
+                              const double adfMatrix[4],
+                              int bboxXMin,
+                              int bboxYMin,
+                              int bboxXMax,
+                              int bboxYMax);
+
+    static void ComputeIntBBox(OGRGeometryH hGeom,
+                           const OGREnvelope& sEnvelope,
+                           const double adfMatrix[4],
+                           const ObjectStyle& os,
+                           double dfRadius,
+                           int& bboxXMin,
+                           int& bboxYMin,
+                           int& bboxXMax,
+                           int& bboxYMax);
+
+    GDALPDFObjectNum  WriteJavascript(const char* pszJavascript, bool bDeflate);
+
+public:
+    GDALPDFObjectNum  SetInfo(GDALDataset* poSrcDS,
+                char** papszOptions);
+    GDALPDFObjectNum  SetInfo(const char* pszAUTHOR,
+                 const char* pszPRODUCER,
+                 const char* pszCREATOR,
+                 const char* pszCREATION_DATE,
+                 const char* pszSUBJECT,
+                 const char* pszTITLE,
+                 const char* pszKEYWORDS);
+    GDALPDFObjectNum  SetXMP(GDALDataset* poSrcDS,
+                const char* pszXMP);
+};
+
+class GDALPDFUpdateWriter final: public GDALPDFBaseWriter
+{
+        bool m_bUpdateNeeded = false;
+        vsi_l_offset m_nLastStartXRef = 0;
+        int m_nLastXRefSize = 0;
+
+public:
+        explicit GDALPDFUpdateWriter( VSILFILE* fpIn );
+       ~GDALPDFUpdateWriter();
 
        void Close();
 
-       int  GetCatalogNum() const { return nCatalogId; }
-       int  GetCatalogGen() const { return nCatalogGen; }
+       const GDALPDFObjectNum& GetCatalogNum() const { return m_nCatalogId; }
+       int  GetCatalogGen() const { return m_nCatalogGen; }
 
        int  ParseTrailerAndXRef();
        void UpdateProj(GDALDataset* poSrcDS,
                        double dfDPI,
                        GDALPDFDictionaryRW* poPageDict,
-                       int nPageNum, int nPageGen);
+                       const GDALPDFObjectNum& nPageId,
+                       int nPageGen);
        void UpdateInfo(GDALDataset* poSrcDS);
        void UpdateXMP (GDALDataset* poSrcDS,
                        GDALPDFDictionaryRW* poCatalogDict);
+};
 
-       int     WriteSRS_ISO32000(GDALDataset* poSrcDS,
-                                 double dfUserUnit,
-                                 const char* pszNEATLINE,
-                                 PDFMargins* psMargins,
-                                 int bWriteViewport);
-       int     WriteSRS_OGC_BP(GDALDataset* poSrcDS,
-                                double dfUserUnit,
-                                const char* pszNEATLINE,
-                                PDFMargins* psMargins);
+class GDALPDFWriter final: public GDALPDFBaseWriter
+{
+    GDALPDFPageContext oPageContext{};
 
-       int  StartPage(GDALDataset* poSrcDS,
+    CPLString    m_osOffLayers{};
+    CPLString    m_osExclusiveLayers{};
+
+    void    WritePages();
+
+    public:
+        explicit GDALPDFWriter( VSILFILE* fpIn );
+       ~GDALPDFWriter();
+
+       void Close();
+
+       bool  StartPage(GDALDataset* poSrcDS,
                       double dfDPI,
                       bool bWriteUserUnit,
                       const char* pszGEO_ENCODING,
@@ -228,7 +364,7 @@
                       PDFCompressMethod eStreamCompressMethod,
                       int bHasOGRData);
 
-       int WriteImagery(GDALDataset* poDS,
+       bool WriteImagery(GDALDataset* poDS,
                         const char* pszLayerName,
                         PDFCompressMethod eCompressMethod,
                         int nPredictor,
@@ -238,7 +374,7 @@
                         GDALProgressFunc pfnProgress,
                         void * pProgressData);
 
-       int WriteClippedImagery(GDALDataset* poDS,
+       bool WriteClippedImagery(GDALDataset* poDS,
                                const char* pszLayerName,
                                PDFCompressMethod eCompressMethod,
                                int nPredictor,
@@ -247,7 +383,7 @@
                                int nBlockXSize, int nBlockYSize,
                                GDALProgressFunc pfnProgress,
                                void * pProgressData);
-       int WriteOGRDataSource(const char* pszOGRDataSource,
+       bool WriteOGRDataSource(const char* pszOGRDataSource,
                               const char* pszOGRDisplayField,
                               const char* pszOGRDisplayLayerNames,
                               const char* pszOGRLinkField,
@@ -271,22 +407,16 @@
                            const char* pszOGRDisplayField,
                            const char* pszOGRLinkField,
                            int bWriteOGRAttributes,
-                           int& iObj,
-                           int& iObjLayer);
+                           int& iObj);
 
-       int  WriteJavascript(const char* pszJavascript);
-       int  WriteJavascriptFile(const char* pszJavascriptFile);
+       GDALPDFObjectNum  WriteJavascript(const char* pszJavascript);
+       GDALPDFObjectNum  WriteJavascriptFile(const char* pszJavascriptFile);
 
        int  EndPage(const char* pszExtraImages,
                     const char* pszExtraStream,
                     const char* pszExtraLayerName,
                     const char* pszOffLayers,
                     const char* pszExclusiveLayers);
-
-       int  SetInfo(GDALDataset* poSrcDS,
-                    char** papszOptions);
-       int  SetXMP(GDALDataset* poSrcDS,
-                   const char* pszXMP);
 };
 
 GDALDataset         *GDALPDFCreateCopy( const char *, GDALDataset *,
diff -urN gdal-2.3.2/frmts/pdf/pdfcreatefromcomposition.cpp gdal-2.3.2-pdf/frmts/pdf/pdfcreatefromcomposition.cpp
--- gdal-2.3.2/frmts/pdf/pdfcreatefromcomposition.cpp	1970-01-01 09:00:00.000000000 +0900
+++ gdal-2.3.2-pdf/frmts/pdf/pdfcreatefromcomposition.cpp	2019-12-12 16:30:14.909312967 +0900
@@ -0,0 +1,2535 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  PDF driver
+ * Purpose:  GDALDataset driver for PDF dataset.
+ * Author:   Even Rouault, <even dot rouault at spatialys dot com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2019, Even Rouault <even dot rouault at spatialys dot com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#include "gdal_pdf.h"
+#include "pdfcreatecopy.h"
+
+#include <cmath>
+#include <cstdlib>
+
+#include "pdfcreatefromcomposition.h"
+#include "cpl_conv.h"
+#include "cpl_minixml.h"
+#include "cpl_vsi_virtual.h"
+#include "ogr_geometry.h"
+
+
+/************************************************************************/
+/*                         GDALPDFComposerWriter()                      */
+/************************************************************************/
+
+GDALPDFComposerWriter::GDALPDFComposerWriter(VSILFILE* fp):
+    GDALPDFBaseWriter(fp)
+{
+    StartNewDoc();
+}
+
+/************************************************************************/
+/*                        ~GDALPDFComposerWriter()                      */
+/************************************************************************/
+
+GDALPDFComposerWriter::~GDALPDFComposerWriter()
+{
+    Close();
+}
+
+/************************************************************************/
+/*                                  Close()                             */
+/************************************************************************/
+
+void GDALPDFComposerWriter::Close()
+{
+    if (m_fp)
+    {
+        CPLAssert(!m_bInWriteObj);
+        if (m_nPageResourceId.toBool())
+        {
+            WritePages();
+            WriteXRefTableAndTrailer(false, 0);
+        }
+    }
+    GDALPDFBaseWriter::Close();
+}
+
+/************************************************************************/
+/*                          CreateOCGOrder()                            */
+/************************************************************************/
+
+GDALPDFArrayRW* GDALPDFComposerWriter::CreateOCGOrder(const TreeOfOCG* parent)
+{
+    auto poArrayOrder = new GDALPDFArrayRW();
+    for( const auto& child: parent->m_children )
+    {
+        poArrayOrder->Add(child->m_nNum, 0);
+        if( !child->m_children.empty() )
+        {
+            poArrayOrder->Add(CreateOCGOrder(child.get()));
+        }
+    }
+    return poArrayOrder;
+}
+
+/************************************************************************/
+/*                          CollectOffOCG()                             */
+/************************************************************************/
+
+void GDALPDFComposerWriter::CollectOffOCG(std::vector<GDALPDFObjectNum>& ar,
+                                          const TreeOfOCG* parent)
+{
+    if( !parent->m_bInitiallyVisible )
+        ar.push_back(parent->m_nNum);
+    for( const auto& child: parent->m_children )
+    {
+        CollectOffOCG(ar, child.get());
+    }
+}
+
+/************************************************************************/
+/*                              WritePages()                            */
+/************************************************************************/
+
+void GDALPDFComposerWriter::WritePages()
+{
+    StartObj(m_nPageResourceId);
+    {
+        GDALPDFDictionaryRW oDict;
+        GDALPDFArrayRW* poKids = new GDALPDFArrayRW();
+        oDict.Add("Type", GDALPDFObjectRW::CreateName("Pages"))
+             .Add("Count", (int)m_asPageId.size())
+             .Add("Kids", poKids);
+
+        for(size_t i=0;i<m_asPageId.size();i++)
+            poKids->Add(m_asPageId[i], 0);
+
+        VSIFPrintfL(m_fp, "%s\n", oDict.Serialize().c_str());
+    }
+    EndObj();
+
+    if (m_nStructTreeRootId.toBool())
+    {
+        auto nParentTreeId = AllocNewObject();
+        StartObj(nParentTreeId);
+        VSIFPrintfL(m_fp, "<< /Nums [ ");
+        for( size_t i = 0; i < m_anParentElements.size(); i++ )
+        {
+            VSIFPrintfL(m_fp, "%d %d 0 R ",
+                        static_cast<int>(i),
+                        m_anParentElements[i].toInt());
+        }
+        VSIFPrintfL(m_fp, " ] >> \n");
+        EndObj();
+
+        StartObj(m_nStructTreeRootId);
+        VSIFPrintfL(m_fp,
+                    "<< "
+                    "/Type /StructTreeRoot "
+                    "/ParentTree %d 0 R "
+                    "/K [ ", nParentTreeId.toInt());
+        for( const auto& num: m_anFeatureLayerId )
+        {
+            VSIFPrintfL(m_fp, "%d 0 R ", num.toInt());
+        }
+        VSIFPrintfL(m_fp,"] >>\n");
+        EndObj();
+    }
+
+    StartObj(m_nCatalogId);
+    {
+        GDALPDFDictionaryRW oDict;
+        oDict.Add("Type", GDALPDFObjectRW::CreateName("Catalog"))
+             .Add("Pages", m_nPageResourceId, 0);
+        if (m_nOutlinesId.toBool())
+            oDict.Add("Outlines", m_nOutlinesId, 0);
+        if (m_nXMPId.toBool())
+            oDict.Add("Metadata", m_nXMPId, 0);
+        if (!m_asOCGs.empty() )
+        {
+            GDALPDFDictionaryRW* poDictOCProperties = new GDALPDFDictionaryRW();
+            oDict.Add("OCProperties", poDictOCProperties);
+
+            GDALPDFDictionaryRW* poDictD = new GDALPDFDictionaryRW();
+            poDictOCProperties->Add("D", poDictD);
+
+            if( m_bDisplayLayersOnlyOnVisiblePages )
+            {
+                poDictD->Add("ListMode",
+                             GDALPDFObjectRW::CreateName("VisiblePages"));
+            }
+
+            /* Build "Order" array of D dict */
+            GDALPDFArrayRW* poArrayOrder = CreateOCGOrder(&m_oTreeOfOGC);
+            poDictD->Add("Order", poArrayOrder);
+
+            /* Build "OFF" array of D dict */
+            std::vector<GDALPDFObjectNum> offOCGs;
+            CollectOffOCG(offOCGs, &m_oTreeOfOGC);
+            if( !offOCGs.empty() )
+            {
+                GDALPDFArrayRW* poArrayOFF = new GDALPDFArrayRW();
+                for( const auto& num: offOCGs )
+                {
+                    poArrayOFF->Add(num, 0);
+                }
+
+                poDictD->Add("OFF", poArrayOFF);
+            }
+
+            /* Build "RBGroups" array of D dict */
+            if( !m_oMapExclusiveOCGIdToOCGs.empty() )
+            {
+                GDALPDFArrayRW* poArrayRBGroups = new GDALPDFArrayRW();
+                for( const auto& group: m_oMapExclusiveOCGIdToOCGs )
+                {
+                    GDALPDFArrayRW* poGroup = new GDALPDFArrayRW();
+                    for( const auto& num: group.second )
+                    {
+                        poGroup->Add(num, 0);
+                    }
+                    poArrayRBGroups->Add(poGroup);
+                }
+
+                poDictD->Add("RBGroups", poArrayRBGroups);
+            }
+
+
+            GDALPDFArrayRW* poArrayOGCs = new GDALPDFArrayRW();
+            for(const auto& ocg: m_asOCGs )
+                poArrayOGCs->Add(ocg.nId, 0);
+            poDictOCProperties->Add("OCGs", poArrayOGCs);
+        }
+
+        if (m_nStructTreeRootId.toBool())
+        {
+            GDALPDFDictionaryRW* poDictMarkInfo = new GDALPDFDictionaryRW();
+            oDict.Add("MarkInfo", poDictMarkInfo);
+            poDictMarkInfo->Add("UserProperties", GDALPDFObjectRW::CreateBool(TRUE));
+
+            oDict.Add("StructTreeRoot", m_nStructTreeRootId, 0);
+        }
+
+        if (m_nNamesId.toBool())
+            oDict.Add("Names", m_nNamesId, 0);
+
+        VSIFPrintfL(m_fp, "%s\n", oDict.Serialize().c_str());
+    }
+    EndObj();
+}
+
+/************************************************************************/
+/*                          CreateLayerTree()                           */
+/************************************************************************/
+
+bool GDALPDFComposerWriter::CreateLayerTree(const CPLXMLNode* psNode,
+                                            const GDALPDFObjectNum& nParentId,
+                                            TreeOfOCG* parent)
+{
+    for(const auto* psIter = psNode->psChild; psIter; psIter = psIter->psNext)
+    {
+        if( psIter->eType == CXT_Element &&
+            strcmp(psIter->pszValue, "Layer") == 0 )
+        {
+            const char* pszId = CPLGetXMLValue(psIter, "id", nullptr);
+            if( !pszId )
+            {
+                CPLError(CE_Failure, CPLE_AppDefined,
+                         "Missing id attribute in Layer");
+                return false;
+            }
+            const char* pszName = CPLGetXMLValue(psIter, "name", nullptr);
+            if( !pszName )
+            {
+                CPLError(CE_Failure, CPLE_AppDefined,
+                         "Missing name attribute in Layer");
+                return false;
+            }
+            if( m_oMapLayerIdToOCG.find(pszId) != m_oMapLayerIdToOCG.end() )
+            {
+                CPLError(CE_Failure, CPLE_AppDefined,
+                         "Layer.id = %s is not unique", pszId);
+                return false;
+            }
+
+            const bool bInitiallyVisible = CPLTestBool(
+                CPLGetXMLValue(psIter, "initiallyVisible", "true"));
+
+            const char* pszMutuallyExclusiveGroupId = CPLGetXMLValue(psIter,
+                                            "mutuallyExclusiveGroupId", nullptr);
+
+            auto nThisObjId = WriteOCG( pszName, nParentId );
+            m_oMapLayerIdToOCG[pszId] = nThisObjId;
+
+            std::unique_ptr<TreeOfOCG> newTreeOfOCG(new TreeOfOCG());
+            newTreeOfOCG->m_nNum = nThisObjId;
+            newTreeOfOCG->m_bInitiallyVisible = bInitiallyVisible;
+            parent->m_children.emplace_back(std::move(newTreeOfOCG));
+
+            if( pszMutuallyExclusiveGroupId )
+            {
+                m_oMapExclusiveOCGIdToOCGs[pszMutuallyExclusiveGroupId].
+                    push_back(nThisObjId);
+            }
+
+            if( !CreateLayerTree(psIter, nThisObjId,
+                                 parent->m_children.back().get()) )
+            {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+
+/************************************************************************/
+/*                             ParseActions()                           */
+/************************************************************************/
+
+bool GDALPDFComposerWriter::ParseActions(const CPLXMLNode* psNode,
+                                std::vector<std::unique_ptr<Action>>& actions)
+{
+    std::set<GDALPDFObjectNum> anONLayers{};
+    std::set<GDALPDFObjectNum> anOFFLayers{};
+    for(const auto* psIter = psNode->psChild; psIter; psIter = psIter->psNext)
+    {
+        if( psIter->eType == CXT_Element &&
+            strcmp(psIter->pszValue, "GotoPageAction") == 0 )
+        {
+            std::unique_ptr<GotoPageAction> poAction(new GotoPageAction());
+            const char* pszPageId = CPLGetXMLValue(psIter, "pageId", nullptr);
+            if( !pszPageId )
+            {
+                CPLError(CE_Failure, CPLE_AppDefined,
+                         "Missing pageId attribute in GotoPageAction");
+                return false;
+            }
+
+            auto oIter = m_oMapPageIdToObjectNum.find(pszPageId);
+            if( oIter == m_oMapPageIdToObjectNum.end() )
+            {
+                CPLError(CE_Failure, CPLE_AppDefined,
+                        "GotoPageAction.pageId = %s not pointing to a Page.id",
+                            pszPageId);
+                return false;
+            }
+            poAction->m_nPageDestId = oIter->second;
+            poAction->m_dfX1 = CPLAtof(CPLGetXMLValue(psIter, "x1", "0"));
+            poAction->m_dfX2 = CPLAtof(CPLGetXMLValue(psIter, "y1", "0"));
+            poAction->m_dfY1 = CPLAtof(CPLGetXMLValue(psIter, "x2", "0"));
+            poAction->m_dfY2 = CPLAtof(CPLGetXMLValue(psIter, "y2", "0"));
+            actions.push_back(std::move(poAction));
+        }
+        else if( psIter->eType == CXT_Element &&
+                 strcmp(psIter->pszValue, "SetAllLayersStateAction") == 0 )
+        {
+            if( CPLTestBool(CPLGetXMLValue(psIter, "visible", "true")) )
+            {
+                for( const auto& ocg: m_asOCGs )
+                {
+                    anOFFLayers.erase(ocg.nId);
+                    anONLayers.insert(ocg.nId);
+                }
+            }
+            else
+            {
+                for( const auto& ocg: m_asOCGs )
+                {
+                    anONLayers.erase(ocg.nId);
+                    anOFFLayers.insert(ocg.nId);
+                }
+            }
+        }
+        else if( psIter->eType == CXT_Element &&
+                 strcmp(psIter->pszValue, "SetLayerStateAction") == 0 )
+        {
+            const char* pszLayerId = CPLGetXMLValue(psIter, "layerId", nullptr);
+            if( !pszLayerId )
+            {
+                CPLError(CE_Failure, CPLE_AppDefined,
+                        "Missing layerId");
+                return false;
+            }
+            auto oIter = m_oMapLayerIdToOCG.find(pszLayerId);
+            if( oIter == m_oMapLayerIdToOCG.end() )
+            {
+                CPLError(CE_Failure, CPLE_AppDefined,
+                        "Referencing layer of unknown id: %s", pszLayerId);
+                return false;
+            }
+            const auto& ocg = oIter->second;
+
+            if( CPLTestBool(CPLGetXMLValue(psIter, "visible", "true")) )
+            {
+                anOFFLayers.erase(ocg);
+                anONLayers.insert(ocg);
+            }
+            else
+            {
+                anONLayers.erase(ocg);
+                anOFFLayers.insert(ocg);
+            }
+        }
+        else if( psIter->eType == CXT_Element &&
+                 strcmp(psIter->pszValue, "JavascriptAction") == 0 )
+        {
+            std::unique_ptr<JavascriptAction> poAction(new JavascriptAction());
+            poAction->m_osScript = CPLGetXMLValue(psIter, nullptr, "");
+            actions.push_back(std::move(poAction));
+        }
+    }
+
+    if( !anONLayers.empty() || !anOFFLayers.empty() )
+    {
+        std::unique_ptr<SetLayerStateAction> poAction(new SetLayerStateAction());
+        poAction->m_anONLayers = std::move(anONLayers);
+        poAction->m_anOFFLayers = std::move(anOFFLayers);
+        actions.push_back(std::move(poAction));
+    }
+
+    return true;
+}
+
+/************************************************************************/
+/*                       CreateOutlineFirstPass()                       */
+/************************************************************************/
+
+bool GDALPDFComposerWriter::CreateOutlineFirstPass(const CPLXMLNode* psNode,
+                                          OutlineItem* poParentItem)
+{
+    for(const auto* psIter = psNode->psChild; psIter; psIter = psIter->psNext)
+    {
+        if( psIter->eType == CXT_Element &&
+            strcmp(psIter->pszValue, "OutlineItem") == 0 )
+        {
+            std::unique_ptr<OutlineItem> newItem(new OutlineItem());
+            const char* pszName = CPLGetXMLValue(psIter, "name", nullptr);
+            if( !pszName )
+            {
+                CPLError(CE_Failure, CPLE_AppDefined,
+                         "Missing name attribute in OutlineItem");
+                return false;
+            }
+            newItem->m_osName = pszName;
+            newItem->m_bOpen =
+                CPLTestBool(CPLGetXMLValue(psIter, "open", "true"));
+            if( CPLTestBool(CPLGetXMLValue(psIter, "italic", "false")) )
+                newItem->m_nFlags |= 1 << 0;
+            if( CPLTestBool(CPLGetXMLValue(psIter, "bold", "false")) )
+                newItem->m_nFlags |= 1 << 1;
+
+            const auto poActions = CPLGetXMLNode(psIter, "Actions");
+            if( poActions )
+            {
+                if( !ParseActions(poActions, newItem->m_aoActions) )
+                    return false;
+            }
+
+            newItem->m_nObjId = AllocNewObject();
+            if( !CreateOutlineFirstPass(psIter, newItem.get()) )
+            {
+                return false;
+            }
+            poParentItem->m_nKidsRecCount += 1 + newItem->m_nKidsRecCount;
+            poParentItem->m_aoKids.push_back(std::move(newItem));
+        }
+    }
+    return true;
+}
+
+/************************************************************************/
+/*                            SerializeActions()                        */
+/************************************************************************/
+
+GDALPDFDictionaryRW* GDALPDFComposerWriter::SerializeActions(
+                        GDALPDFDictionaryRW* poDictForDest,
+                        const std::vector<std::unique_ptr<Action>>& actions)
+{
+    GDALPDFDictionaryRW* poRetAction = nullptr;
+    GDALPDFDictionaryRW* poLastActionDict = nullptr;
+    for( const auto& poAction: actions )
+    {
+        GDALPDFDictionaryRW* poActionDict = nullptr;
+        auto poGotoPageAction = dynamic_cast<GotoPageAction*>(poAction.get());
+        if( poGotoPageAction )
+        {
+            GDALPDFArrayRW* poDest = new GDALPDFArrayRW;
+            poDest->Add(poGotoPageAction->m_nPageDestId, 0);
+            if( poGotoPageAction->m_dfX1 == 0.0 &&
+                poGotoPageAction->m_dfX2 == 0.0 &&
+                poGotoPageAction->m_dfY1 == 0.0 &&
+                poGotoPageAction->m_dfY2 == 0.0 )
+            {
+                poDest->Add(GDALPDFObjectRW::CreateName("XYZ"))
+                        .Add(GDALPDFObjectRW::CreateNull())
+                        .Add(GDALPDFObjectRW::CreateNull())
+                        .Add(GDALPDFObjectRW::CreateNull());
+            }
+            else
+            {
+                poDest->Add(GDALPDFObjectRW::CreateName("FitR"))
+                        .Add(poGotoPageAction->m_dfX1)
+                        .Add(poGotoPageAction->m_dfY1)
+                        .Add(poGotoPageAction->m_dfX2)
+                        .Add(poGotoPageAction->m_dfY2);
+            }
+            if( poDictForDest && actions.size() == 1 )
+            {
+                poDictForDest->Add("Dest", poDest);
+            }
+            else
+            {
+                poActionDict = new GDALPDFDictionaryRW();
+                poActionDict->Add("Type", GDALPDFObjectRW::CreateName("Action"));
+                poActionDict->Add("S", GDALPDFObjectRW::CreateName("GoTo"));
+                poActionDict->Add("D", poDest);
+            }
+        }
+
+        auto setLayerStateAction = dynamic_cast<SetLayerStateAction*>(poAction.get());
+        if( poActionDict == nullptr && setLayerStateAction )
+        {
+            poActionDict = new GDALPDFDictionaryRW();
+            poActionDict->Add("Type", GDALPDFObjectRW::CreateName("Action"));
+            poActionDict->Add("S", GDALPDFObjectRW::CreateName("SetOCGState"));
+            auto poStateArray = new GDALPDFArrayRW();
+            if( !setLayerStateAction->m_anOFFLayers.empty() )
+            {
+                poStateArray->Add(GDALPDFObjectRW::CreateName("OFF"));
+                for( const auto& ocg: setLayerStateAction->m_anOFFLayers )
+                    poStateArray->Add(ocg, 0);
+            }
+            if( !setLayerStateAction->m_anONLayers.empty() )
+            {
+                poStateArray->Add(GDALPDFObjectRW::CreateName("ON"));
+                for( const auto& ocg: setLayerStateAction->m_anONLayers )
+                    poStateArray->Add(ocg, 0);
+            }
+            poActionDict->Add("State", poStateArray);
+        }
+
+        auto javascriptAction = dynamic_cast<JavascriptAction*>(poAction.get());
+        if( poActionDict == nullptr && javascriptAction )
+        {
+            poActionDict = new GDALPDFDictionaryRW();
+            poActionDict->Add("Type", GDALPDFObjectRW::CreateName("Action"));
+            poActionDict->Add("S", GDALPDFObjectRW::CreateName("JavaScript"));
+            poActionDict->Add("JS", javascriptAction->m_osScript);
+        }
+
+        if( poActionDict )
+        {
+            if( poLastActionDict == nullptr )
+            {
+                poRetAction = poActionDict;
+            }
+            else
+            {
+                poLastActionDict->Add("Next", poActionDict);
+            }
+            poLastActionDict = poActionDict;
+        }
+    }
+    return poRetAction;
+}
+
+/************************************************************************/
+/*                        SerializeOutlineKids()                        */
+/************************************************************************/
+
+bool GDALPDFComposerWriter::SerializeOutlineKids(const OutlineItem* poParentItem)
+{
+    for( size_t i = 0; i < poParentItem->m_aoKids.size(); i++ )
+    {
+        const auto& poItem = poParentItem->m_aoKids[i];
+        StartObj(poItem->m_nObjId);
+        GDALPDFDictionaryRW oDict;
+        oDict.Add("Title", poItem->m_osName);
+
+        auto poActionDict = SerializeActions(&oDict, poItem->m_aoActions);
+        if( poActionDict )
+        {
+            oDict.Add("A", poActionDict);
+        }
+
+        if( i > 0 )
+        {
+            oDict.Add("Prev", poParentItem->m_aoKids[i-1]->m_nObjId, 0);
+        }
+        if( i + 1 < poParentItem->m_aoKids.size() )
+        {
+            oDict.Add("Next", poParentItem->m_aoKids[i+1]->m_nObjId, 0);
+        }
+        if( poItem->m_nFlags )
+            oDict.Add("F", poItem->m_nFlags);
+        oDict.Add("Parent", poParentItem->m_nObjId, 0);
+        if( !poItem->m_aoKids.empty() )
+        {
+            oDict.Add("First", poItem->m_aoKids.front()->m_nObjId, 0);
+            oDict.Add("Last", poItem->m_aoKids.back()->m_nObjId, 0);
+            oDict.Add("Count", poItem->m_bOpen ?
+                poItem->m_nKidsRecCount : -poItem->m_nKidsRecCount);
+        }
+        VSIFPrintfL(m_fp, "%s\n", oDict.Serialize().c_str());
+        EndObj();
+        SerializeOutlineKids(poItem.get());
+    }
+    return true;
+}
+
+/************************************************************************/
+/*                           CreateOutline()                            */
+/************************************************************************/
+
+bool GDALPDFComposerWriter::CreateOutline(const CPLXMLNode* psNode)
+{
+    OutlineItem oRootOutlineItem;
+    if( !CreateOutlineFirstPass(psNode, &oRootOutlineItem) )
+        return false;
+    if( oRootOutlineItem.m_aoKids.empty() )
+        return true;
+
+    m_nOutlinesId = AllocNewObject();
+    StartObj(m_nOutlinesId);
+    GDALPDFDictionaryRW oDict;
+    oDict.Add("Type", GDALPDFObjectRW::CreateName("Outlines"))
+         .Add("First", oRootOutlineItem.m_aoKids.front()->m_nObjId, 0)
+         .Add("Last", oRootOutlineItem.m_aoKids.back()->m_nObjId, 0)
+         .Add("Count", oRootOutlineItem.m_nKidsRecCount);
+    VSIFPrintfL(m_fp, "%s\n", oDict.Serialize().c_str());
+    EndObj();
+    oRootOutlineItem.m_nObjId = m_nOutlinesId;
+    return SerializeOutlineKids(&oRootOutlineItem);
+}
+
+/************************************************************************/
+/*                        GenerateGeoreferencing()                      */
+/************************************************************************/
+
+
+bool GDALPDFComposerWriter::GenerateGeoreferencing(const CPLXMLNode* psGeoreferencing,
+                                                   double dfWidthInUserUnit,
+                                                   double dfHeightInUserUnit,
+                                                   GDALPDFObjectNum& nViewportId,
+                                                   GDALPDFObjectNum& nLGIDictId,
+                                                   Georeferencing& georeferencing)
+{
+    double bboxX1 = 0;
+    double bboxY1 = 0;
+    double bboxX2 = dfWidthInUserUnit;
+    double bboxY2 = dfHeightInUserUnit;
+    const auto psBoundingBox = CPLGetXMLNode(psGeoreferencing, "BoundingBox");
+    if( psBoundingBox )
+    {
+        bboxX1 = CPLAtof(
+            CPLGetXMLValue(psBoundingBox, "x1", CPLSPrintf("%.18g", bboxX1)));
+        bboxY1 = CPLAtof(
+            CPLGetXMLValue(psBoundingBox, "y1", CPLSPrintf("%.18g", bboxY1)));
+        bboxX2 = CPLAtof(
+            CPLGetXMLValue(psBoundingBox, "x2", CPLSPrintf("%.18g", bboxX2)));
+        bboxY2 = CPLAtof(
+            CPLGetXMLValue(psBoundingBox, "y2", CPLSPrintf("%.18g", bboxY2)));
+        if( bboxX2 <= bboxX1 || bboxY2 <= bboxY1 )
+        {
+            CPLError(CE_Failure, CPLE_AppDefined, "Invalid BoundingBox");
+            return false;
+        }
+    }
+
+    std::vector<GDAL_GCP> aGCPs;
+    for(const auto* psIter = psGeoreferencing->psChild;
+        psIter; psIter = psIter->psNext)
+    {
+        if( psIter->eType == CXT_Element &&
+            strcmp(psIter->pszValue, "ControlPoint") == 0 )
+        {
+            const char* pszx = CPLGetXMLValue(psIter, "x", nullptr);
+            const char* pszy = CPLGetXMLValue(psIter, "y", nullptr);
+            const char* pszX = CPLGetXMLValue(psIter, "GeoX", nullptr);
+            const char* pszY = CPLGetXMLValue(psIter, "GeoY", nullptr);
+            if( !pszx || !pszy || !pszX || !pszY )
+            {
+                CPLError(CE_Failure, CPLE_NotSupported,
+                         "At least one of x, y, GeoX or GeoY attribute "
+                         "missing on ControlPoint");
+                return false;
+            }
+            GDAL_GCP gcp;
+            gcp.pszId = nullptr;
+            gcp.pszInfo = nullptr;
+            gcp.dfGCPPixel = CPLAtof(pszx);
+            gcp.dfGCPLine = CPLAtof(pszy);
+            gcp.dfGCPX = CPLAtof(pszX);
+            gcp.dfGCPY = CPLAtof(pszY);
+            gcp.dfGCPZ = 0;
+            aGCPs.emplace_back(std::move(gcp));
+        }
+    }
+    if( aGCPs.size() < 4 )
+    {
+        CPLError(CE_Failure, CPLE_NotSupported,
+                 "At least 4 ControlPoint are required");
+        return false;
+    }
+
+    const char* pszBoundingPolygon = 
+        CPLGetXMLValue(psGeoreferencing, "BoundingPolygon", nullptr);
+    std::vector<xyPair> aBoundingPolygon;
+    if( pszBoundingPolygon )
+    {
+        OGRGeometry* poGeom = nullptr;
+        OGRGeometryFactory::createFromWkt(pszBoundingPolygon, nullptr, &poGeom);
+        if( poGeom && poGeom->getGeometryType() == wkbPolygon )
+        {
+            auto poPoly = poGeom->toPolygon();
+            auto poRing = poPoly->getExteriorRing();
+            if( poRing )
+            {
+                if( psBoundingBox == nullptr )
+                {
+                    OGREnvelope sEnvelope;
+                    poRing->getEnvelope(&sEnvelope);
+                    bboxX1 = sEnvelope.MinX;
+                    bboxY1 = sEnvelope.MinY;
+                    bboxX2 = sEnvelope.MaxX;
+                    bboxY2 = sEnvelope.MaxY;
+                }
+                for( int i = 0; i < poRing->getNumPoints(); i++ )
+                {
+                    aBoundingPolygon.emplace_back(
+                        xyPair(poRing->getX(i), poRing->getY(i)));
+                }
+            }
+        }
+        delete poGeom;
+    }
+
+    const auto pszSRS = CPLGetXMLValue(psGeoreferencing, "SRS", nullptr);
+    if( !pszSRS )
+    {
+        CPLError(CE_Failure, CPLE_NotSupported,
+                 "Missing SRS");
+        return false;
+    }
+    std::unique_ptr<OGRSpatialReference> poSRS(new OGRSpatialReference());
+    if( poSRS->SetFromUserInput(pszSRS) != OGRERR_NONE )
+    {
+        return false;
+    }
+    // poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); // Since GDAL 3.0
+
+    if( CPLTestBool(CPLGetXMLValue(psGeoreferencing, "ISO32000ExtensionFormat", "true")) )
+    {
+        nViewportId = GenerateISO32000_Georeferencing(
+            OGRSpatialReference::ToHandle(poSRS.get()),
+            bboxX1, bboxY1, bboxX2, bboxY2, aGCPs, aBoundingPolygon);
+        if( !nViewportId.toBool() )
+        {
+            return false;
+        }
+    }
+
+    if( CPLTestBool(CPLGetXMLValue(psGeoreferencing, "OGCBestPracticeFormat", "false")) )
+    {
+        nLGIDictId = GenerateOGC_BP_Georeferencing(
+            OGRSpatialReference::ToHandle(poSRS.get()),
+            bboxX1, bboxY1, bboxX2, bboxY2, aGCPs, aBoundingPolygon);
+        if( !nLGIDictId.toBool() )
+        {
+            return false;
+        }
+    }
+
+    const char* pszId = CPLGetXMLValue(psGeoreferencing, "id", nullptr);
+    if( pszId )
+    {
+        if (!GDALGCPsToGeoTransform( static_cast<int>(aGCPs.size()),
+                                     aGCPs.data(),
+                                     georeferencing.m_adfGT, TRUE))
+        {
+            CPLError(CE_Failure, CPLE_AppDefined,
+                     "Could not compute geotransform with approximate match.");
+            return false;
+        }
+        if( std::fabs(georeferencing.m_adfGT[2]) < 1e-5 *
+                    std::fabs(georeferencing.m_adfGT[1]) &&
+            std::fabs(georeferencing.m_adfGT[4]) < 1e-5 *
+                    std::fabs(georeferencing.m_adfGT[5]) )
+        {
+            georeferencing.m_adfGT[2] = 0;
+            georeferencing.m_adfGT[4] = 0;
+        }
+        if( georeferencing.m_adfGT[2] != 0 ||
+            georeferencing.m_adfGT[4] != 0 ||
+            georeferencing.m_adfGT[5] < 0 )
+        {
+            CPLError(CE_Failure, CPLE_AppDefined,
+                     "Geotransform should define a north-up non rotated area.");
+            return false;
+        }
+        georeferencing.m_osID = pszId;
+        georeferencing.m_oSRS = *(poSRS.get());
+        georeferencing.m_bboxX1 = bboxX1;
+        georeferencing.m_bboxY1 = bboxY1;
+        georeferencing.m_bboxX2 = bboxX2;
+        georeferencing.m_bboxY2 = bboxY2;
+    }
+
+    return true;
+}
+
+/************************************************************************/
+/*                      GenerateISO32000_Georeferencing()               */
+/************************************************************************/
+
+GDALPDFObjectNum GDALPDFComposerWriter::GenerateISO32000_Georeferencing(
+    OGRSpatialReferenceH hSRS,
+    double bboxX1, double bboxY1, double bboxX2, double bboxY2,
+    const std::vector<GDAL_GCP>& aGCPs,
+    const std::vector<xyPair>& aBoundingPolygon)
+{
+    OGRSpatialReferenceH hSRSGeog = OSRCloneGeogCS(hSRS);
+    if( hSRSGeog == nullptr )
+    {
+        return GDALPDFObjectNum();
+    }
+    // OSRSetAxisMappingStrategy(hSRSGeog, OAMS_TRADITIONAL_GIS_ORDER); // Since GDAL 3.0
+    OGRCoordinateTransformationH hCT = OCTNewCoordinateTransformation( hSRS, hSRSGeog);
+    if( hCT == nullptr )
+    {
+        OSRDestroySpatialReference(hSRSGeog);
+        return GDALPDFObjectNum();
+    }
+
+    std::vector<GDAL_GCP> aGCPReprojected;
+    bool bSuccess = true;
+    for( const auto& gcp: aGCPs )
+    {
+        double X = gcp.dfGCPX;
+        double Y = gcp.dfGCPY;
+        bSuccess &= OCTTransform( hCT, 1,&X, &Y, nullptr ) == 1;
+        GDAL_GCP newGCP;
+        newGCP.pszId = nullptr;
+        newGCP.pszInfo = nullptr;
+        newGCP.dfGCPPixel = gcp.dfGCPPixel;
+        newGCP.dfGCPLine = gcp.dfGCPLine;
+        newGCP.dfGCPX = X;
+        newGCP.dfGCPY = Y;
+        newGCP.dfGCPZ = 0;
+        aGCPReprojected.emplace_back(std::move(newGCP));
+    }
+    if( !bSuccess )
+    {
+        OSRDestroySpatialReference(hSRSGeog);
+        OCTDestroyCoordinateTransformation(hCT);
+
+        return GDALPDFObjectNum();
+    }
+
+    const char * pszAuthorityCode = OSRGetAuthorityCode( hSRS, nullptr );
+    const char * pszAuthorityName = OSRGetAuthorityName( hSRS, nullptr );
+    int nEPSGCode = 0;
+    if( pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG") &&
+        pszAuthorityCode != nullptr )
+        nEPSGCode = atoi(pszAuthorityCode);
+
+    int bIsGeographic = OSRIsGeographic(hSRS);
+
+    char* pszESRIWKT = nullptr;
+    const char* apszOptions[] = { "FORMAT=WKT1_ESRI", nullptr };
+
+#if 1
+    //OSRStripCTParms(hSRS, &pszESRIWKT, apszOptions);
+    {
+       auto wktFormat = PJ_WKT1_ESRI;
+       const char* pszFormat = CSLFetchNameValueDef(papszOptions, "FORMAT",
+                                    CPLGetConfigOption("OSR_WKT_FORMAT", ""));
+       CPLStringList aosOptions;
+       aosOptions.SetNameValue("MULTILINE",
+                    CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO"));
+
+       auto ctxt = d->getPROJContext();
+       const char* pszWKT = proj_as_wkt(
+                  ctxt, d->m_pj_crs,
+                  wktFormat, aosOptions.List());
+
+       pszESRIWKT = CPLStrdup( pszWKT );
+    }
+#else
+    // Since GDAL 3.0
+    OSRExportToWktEx(hSRS, &pszESRIWKT, apszOptions);
+#endif
+
+    OSRDestroySpatialReference(hSRSGeog);
+    OCTDestroyCoordinateTransformation(hCT);
+
+    auto nViewportId = AllocNewObject();
+    auto nMeasureId = AllocNewObject();
+    auto nGCSId = AllocNewObject();
+
+    StartObj(nViewportId);
+    GDALPDFDictionaryRW oViewPortDict;
+    oViewPortDict.Add("Type", GDALPDFObjectRW::CreateName("Viewport"))
+                .Add("Name", "Layer")
+                .Add("BBox", &((new GDALPDFArrayRW())
+                                ->Add(bboxX1).Add(bboxY1)
+                                 .Add(bboxX2).Add(bboxY2)))
+                .Add("Measure", nMeasureId, 0);
+    VSIFPrintfL(m_fp, "%s\n", oViewPortDict.Serialize().c_str());
+    EndObj();
+
+    GDALPDFArrayRW* poGPTS = new GDALPDFArrayRW();
+    GDALPDFArrayRW* poLPTS = new GDALPDFArrayRW();
+
+    const int nPrecision =
+        atoi(CPLGetConfigOption("PDF_COORD_DOUBLE_PRECISION", "16"));
+    for( const auto& gcp: aGCPReprojected )
+    {
+        poGPTS->AddWithPrecision(gcp.dfGCPY, nPrecision).
+                AddWithPrecision(gcp.dfGCPX, nPrecision); // Lat, long order
+        poLPTS->AddWithPrecision((gcp.dfGCPPixel - bboxX1) / (bboxX2 - bboxX1), nPrecision).
+                AddWithPrecision((gcp.dfGCPLine - bboxY1) / (bboxY2 - bboxY1), nPrecision);
+    }
+
+    StartObj(nMeasureId);
+    GDALPDFDictionaryRW oMeasureDict;
+    oMeasureDict .Add("Type", GDALPDFObjectRW::CreateName("Measure"))
+                 .Add("Subtype", GDALPDFObjectRW::CreateName("GEO"))
+                 .Add("GPTS", poGPTS)
+                 .Add("LPTS", poLPTS)
+                 .Add("GCS", nGCSId, 0);
+    if( !aBoundingPolygon.empty() )
+    {
+        GDALPDFArrayRW* poBounds = new GDALPDFArrayRW();
+        for( const auto& xy: aBoundingPolygon )
+        {
+             poBounds->Add((xy.x - bboxX1) / (bboxX2 - bboxX1)).
+                       Add((xy.y - bboxY1) / (bboxY2 - bboxY1));
+        }
+        oMeasureDict.Add("Bounds", poBounds);
+    }
+    VSIFPrintfL(m_fp, "%s\n", oMeasureDict.Serialize().c_str());
+    EndObj();
+
+    StartObj(nGCSId);
+    GDALPDFDictionaryRW oGCSDict;
+    oGCSDict.Add("Type", GDALPDFObjectRW::CreateName(bIsGeographic ? "GEOGCS" : "PROJCS"))
+            .Add("WKT", pszESRIWKT);
+    if (nEPSGCode)
+        oGCSDict.Add("EPSG", nEPSGCode);
+    VSIFPrintfL(m_fp, "%s\n", oGCSDict.Serialize().c_str());
+    EndObj();
+
+    CPLFree(pszESRIWKT);
+
+    return nViewportId;
+}
+
+/************************************************************************/
+/*                      GenerateOGC_BP_Georeferencing()                 */
+/************************************************************************/
+
+GDALPDFObjectNum GDALPDFComposerWriter::GenerateOGC_BP_Georeferencing(
+    OGRSpatialReferenceH hSRS,
+    double bboxX1, double bboxY1, double bboxX2, double bboxY2,
+    const std::vector<GDAL_GCP>& aGCPs,
+    const std::vector<xyPair>& aBoundingPolygon)
+{
+    const OGRSpatialReference* poSRS = OGRSpatialReference::FromHandle(hSRS);
+    GDALPDFDictionaryRW* poProjectionDict = GDALPDFBuildOGC_BP_Projection(poSRS);
+    if (poProjectionDict == nullptr)
+    {
+        OSRDestroySpatialReference(hSRS);
+        return GDALPDFObjectNum();
+    }
+
+    GDALPDFArrayRW* poNeatLineArray = new GDALPDFArrayRW();
+    if( !aBoundingPolygon.empty() )
+    {
+        for( const auto& xy: aBoundingPolygon )
+        {
+             poNeatLineArray->Add(xy.x).Add(xy.y);
+        }
+    }
+    else
+    {
+        poNeatLineArray->Add(bboxX1).Add(bboxY1).
+                         Add(bboxX2).Add(bboxY2);
+    }
+
+    GDALPDFArrayRW* poRegistration = new GDALPDFArrayRW();
+
+    for( const auto& gcp: aGCPs )
+    {
+        GDALPDFArrayRW* poGCP = new GDALPDFArrayRW();
+        poGCP->Add(gcp.dfGCPPixel, TRUE).Add(gcp.dfGCPLine, TRUE).
+               Add(gcp.dfGCPX, TRUE).Add(gcp.dfGCPY, TRUE);
+        poRegistration->Add(poGCP);
+    }
+
+    auto nLGIDictId = AllocNewObject();
+    StartObj(nLGIDictId);
+    GDALPDFDictionaryRW oLGIDict;
+    oLGIDict.Add("Type", GDALPDFObjectRW::CreateName("LGIDict"))
+            .Add("Version", "2.1")
+            .Add("Neatline", poNeatLineArray);
+
+    oLGIDict.Add("Registration", poRegistration);
+
+    /* GDAL extension */
+    if( CPLTestBool( CPLGetConfigOption("GDAL_PDF_OGC_BP_WRITE_WKT", "TRUE") ) )
+    {
+        char* pszWKT = nullptr;
+        OSRExportToWkt(hSRS, &pszWKT);
+        if( pszWKT )
+            poProjectionDict->Add("WKT", pszWKT);
+        CPLFree(pszWKT);
+    }
+
+    oLGIDict.Add("Projection", poProjectionDict);
+
+    VSIFPrintfL(m_fp, "%s\n", oLGIDict.Serialize().c_str());
+    EndObj();
+
+    return nLGIDictId;
+}
+
+/************************************************************************/
+/*                         GeneratePage()                               */
+/************************************************************************/
+
+
+bool GDALPDFComposerWriter::GeneratePage(const CPLXMLNode* psPage)
+{
+    double dfWidthInUserUnit = CPLAtof(CPLGetXMLValue(psPage, "Width", "-1"));
+    double dfHeightInUserUnit = CPLAtof(CPLGetXMLValue(psPage, "Height", "-1"));
+    if( dfWidthInUserUnit <= 0 || dfWidthInUserUnit >= MAXIMUM_SIZE_IN_UNITS ||
+        dfHeightInUserUnit <= 0 || dfHeightInUserUnit >= MAXIMUM_SIZE_IN_UNITS )
+    {
+        CPLError(CE_Failure, CPLE_AppDefined,
+                 "Missing or invalid Width and/or Height");
+        return false;
+    }
+    double dfUserUnit = CPLAtof(CPLGetXMLValue(psPage, "DPI",
+                        CPLSPrintf("%f", DEFAULT_DPI))) * USER_UNIT_IN_INCH;
+
+    std::vector<GDALPDFObjectNum> anViewportIds;
+    std::vector<GDALPDFObjectNum> anLGIDictIds;
+
+    PageContext oPageContext;
+    for(const auto* psIter = psPage->psChild; psIter; psIter = psIter->psNext)
+    {
+        if( psIter->eType == CXT_Element &&
+            strcmp(psIter->pszValue, "Georeferencing") == 0 )
+        {
+            GDALPDFObjectNum nViewportId;
+            GDALPDFObjectNum nLGIDictId;
+            Georeferencing georeferencing;
+            if( !GenerateGeoreferencing(psIter,
+                                        dfWidthInUserUnit, dfHeightInUserUnit,
+                                        nViewportId, nLGIDictId,
+                                        georeferencing) )
+            {
+                return false;
+            }
+            if( nViewportId.toBool() )
+                anViewportIds.emplace_back(nViewportId);
+            if( nLGIDictId.toBool() )
+                anLGIDictIds.emplace_back(nLGIDictId);
+            if( !georeferencing.m_osID.empty() )
+            {
+                oPageContext.m_oMapGeoreferencedId[georeferencing.m_osID] = 
+                    georeferencing;
+            }
+        }
+    }
+
+    auto nPageId = AllocNewObject();
+    m_asPageId.push_back(nPageId);
+
+    const char* pszId = CPLGetXMLValue(psPage, "id", nullptr);
+    if( pszId )
+    {
+        if( m_oMapPageIdToObjectNum.find(pszId) != m_oMapPageIdToObjectNum.end() )
+        {
+            CPLError(CE_Failure, CPLE_AppDefined,
+                     "Duplicated page id %s", pszId);
+            return false;
+        }
+        m_oMapPageIdToObjectNum[pszId] = nPageId;
+    }
+
+    const auto psContent = CPLGetXMLNode(psPage, "Content");
+    if( !psContent )
+    {
+        CPLError(CE_Failure, CPLE_AppDefined, "Missing Content");
+        return false;
+    }
+
+    const bool bDeflateStreamCompression = EQUAL(
+        CPLGetXMLValue(psContent, "streamCompression", "DEFLATE"), "DEFLATE");
+
+    oPageContext.m_dfWidthInUserUnit = dfWidthInUserUnit;
+    oPageContext.m_dfHeightInUserUnit = dfHeightInUserUnit;
+    oPageContext.m_eStreamCompressMethod =
+        bDeflateStreamCompression ? COMPRESS_DEFLATE : COMPRESS_NONE;
+    if( !ExploreContent(psContent, oPageContext) )
+        return false;
+
+    int nStructParentsIdx = -1;
+    if( !oPageContext.m_anFeatureUserProperties.empty() )
+    {
+        nStructParentsIdx = static_cast<int>(m_anParentElements.size());
+        auto nParentsElements = AllocNewObject();
+        m_anParentElements.push_back(nParentsElements);
+        {
+            StartObj(nParentsElements);
+            VSIFPrintfL(m_fp, "[ ");
+            for( const auto& num: oPageContext.m_anFeatureUserProperties )
+                VSIFPrintfL(m_fp, "%d 0 R ", num.toInt());
+            VSIFPrintfL(m_fp, " ]\n");
+            EndObj();
+        }
+    }
+
+    GDALPDFObjectNum nAnnotsId;
+    if( !oPageContext.m_anAnnotationsId.empty() )
+    {
+        /* -------------------------------------------------------------- */
+        /*  Write annotation arrays.                                      */
+        /* -------------------------------------------------------------- */
+        nAnnotsId = AllocNewObject();
+        StartObj(nAnnotsId);
+        {
+            GDALPDFArrayRW oArray;
+            for(size_t i = 0; i < oPageContext.m_anAnnotationsId.size(); i++)
+            {
+                oArray.Add(oPageContext.m_anAnnotationsId[i], 0);
+            }
+            VSIFPrintfL(m_fp, "%s\n", oArray.Serialize().c_str());
+        }
+        EndObj();
+    }
+
+    auto nContentId = AllocNewObject();
+    auto nResourcesId = AllocNewObject();
+
+    StartObj(nPageId);
+    GDALPDFDictionaryRW oDictPage;
+    oDictPage.Add("Type", GDALPDFObjectRW::CreateName("Page"))
+             .Add("Parent", m_nPageResourceId, 0)
+             .Add("MediaBox", &((new GDALPDFArrayRW())
+                               ->Add(0).Add(0).
+                                 Add(dfWidthInUserUnit).
+                                 Add(dfHeightInUserUnit)))
+             .Add("UserUnit", dfUserUnit)
+             .Add("Contents", nContentId, 0)
+             .Add("Resources", nResourcesId, 0);
+
+    if( nAnnotsId.toBool() )
+        oDictPage.Add("Annots", nAnnotsId, 0);
+
+    oDictPage.Add("Group",
+                    &((new GDALPDFDictionaryRW())
+                    ->Add("Type", GDALPDFObjectRW::CreateName("Group"))
+                        .Add("S", GDALPDFObjectRW::CreateName("Transparency"))
+                        .Add("CS", GDALPDFObjectRW::CreateName("DeviceRGB"))));
+    if (!anViewportIds.empty())
+    {
+        auto poViewports = new GDALPDFArrayRW();
+        for( const auto& id: anViewportIds )
+            poViewports->Add(id, 0);
+        oDictPage.Add("VP", poViewports);
+    }
+
+    if (anLGIDictIds.size() == 1 )
+    {
+        oDictPage.Add("LGIDict", anLGIDictIds[0], 0);
+    }
+    else if (!anLGIDictIds.empty())
+    {
+        auto poLGIDict = new GDALPDFArrayRW();
+        for( const auto& id: anLGIDictIds )
+            poLGIDict->Add(id, 0);
+        oDictPage.Add("LGIDict", poLGIDict);
+    }
+
+    if( nStructParentsIdx >= 0 )
+    {
+        oDictPage.Add("StructParents", nStructParentsIdx);
+    }
+
+    VSIFPrintfL(m_fp, "%s\n", oDictPage.Serialize().c_str());
+    EndObj();
+
+    /* -------------------------------------------------------------- */
+    /*  Write content dictionary                                      */
+    /* -------------------------------------------------------------- */
+    {
+        GDALPDFDictionaryRW oDict;
+        StartObjWithStream(nContentId, oDict, bDeflateStreamCompression);
+        VSIFPrintfL(m_fp, "%s", oPageContext.m_osDrawingStream.c_str());
+        EndObjWithStream();
+    }
+
+    /* -------------------------------------------------------------- */
+    /*  Write page resource dictionary.                               */
+    /* -------------------------------------------------------------- */
+    StartObj(nResourcesId);
+    {
+        GDALPDFDictionaryRW oDict;
+        if( !oPageContext.m_oXObjects.empty() )
+        {
+            GDALPDFDictionaryRW* poDict = new GDALPDFDictionaryRW();
+            for( const auto&kv: oPageContext.m_oXObjects )
+            {
+                poDict->Add(kv.first, kv.second, 0);
+            }
+            oDict.Add("XObject", poDict);
+        }
+
+        if( !oPageContext.m_oProperties.empty() )
+        {
+            GDALPDFDictionaryRW* poDict = new GDALPDFDictionaryRW();
+            for( const auto&kv: oPageContext.m_oProperties )
+            {
+                poDict->Add(kv.first, kv.second, 0);
+            }
+            oDict.Add("Properties", poDict);
+        }
+
+        if( !oPageContext.m_oExtGState.empty() )
+        {
+            GDALPDFDictionaryRW* poDict = new GDALPDFDictionaryRW();
+            for( const auto&kv: oPageContext.m_oExtGState )
+            {
+                poDict->Add(kv.first, kv.second, 0);
+            }
+            oDict.Add("ExtGState", poDict);
+        }
+
+        VSIFPrintfL(m_fp, "%s\n", oDict.Serialize().c_str());
+    }
+    EndObj();
+
+    return true;
+}
+
+/************************************************************************/
+/*                          ExploreContent()                            */
+/************************************************************************/
+
+bool GDALPDFComposerWriter::ExploreContent(const CPLXMLNode* psNode,
+                                           PageContext& oPageContext)
+{
+    for(const auto* psIter = psNode->psChild; psIter; psIter = psIter->psNext)
+    {
+        if( psIter->eType == CXT_Element &&
+            strcmp(psIter->pszValue, "IfLayerOn") == 0 )
+        {
+            const char* pszLayerId = CPLGetXMLValue(psIter, "layerId", nullptr);
+            if( !pszLayerId )
+            {
+                CPLError(CE_Failure, CPLE_AppDefined,
+                        "Missing layerId");
+                return false;
+            }
+            auto oIter = m_oMapLayerIdToOCG.find(pszLayerId);
+            if( oIter == m_oMapLayerIdToOCG.end() )
+            {
+                CPLError(CE_Failure, CPLE_AppDefined,
+                        "Referencing layer of unknown id: %s", pszLayerId);
+                return false;
+            }
+            oPageContext.m_oProperties[
+                CPLOPrintf("Lyr%d", oIter->second.toInt())] = oIter->second;
+            oPageContext.m_osDrawingStream +=
+                CPLOPrintf("/OC /Lyr%d BDC\n", oIter->second.toInt());
+            if( !ExploreContent(psIter, oPageContext) )
+                return false;
+            oPageContext.m_osDrawingStream += "EMC\n";
+        }
+
+        else if( psIter->eType == CXT_Element &&
+            strcmp(psIter->pszValue, "Raster") == 0 )
+        {
+            if( !WriteRaster(psIter, oPageContext) )
+                return false;
+        }
+
+        else if( psIter->eType == CXT_Element &&
+            strcmp(psIter->pszValue, "Vector") == 0 )
+        {
+            if( !WriteVector(psIter, oPageContext) )
+                return false;
+        }
+
+        else if( psIter->eType == CXT_Element &&
+            strcmp(psIter->pszValue, "VectorLabel") == 0 )
+        {
+            if( !WriteVectorLabel(psIter, oPageContext) )
+                return false;
+        }
+
+        else if( psIter->eType == CXT_Element &&
+            strcmp(psIter->pszValue, "PDF") == 0 )
+        {
+#ifdef HAVE_PDF_READ_SUPPORT
+            if( !WritePDF(psIter, oPageContext) )
+                return false;
+#else
+            CPLError(CE_Failure, CPLE_NotSupported,
+                    "PDF node not supported due to missing PDF read support in this GDAL build");
+            return false;
+#endif
+        }
+    }
+    return true;
+}
+
+/************************************************************************/
+/*                          StartBlending()                             */
+/************************************************************************/
+
+void GDALPDFComposerWriter::StartBlending(const CPLXMLNode* psNode,
+                                          PageContext& oPageContext,
+                                          double& dfOpacity)
+{
+    dfOpacity = 1;
+    const auto psBlending = CPLGetXMLNode(psNode, "Blending");
+    if( psBlending )
+    {
+        auto nExtGState = AllocNewObject();
+        StartObj(nExtGState);
+        {
+            GDALPDFDictionaryRW gs;
+            gs.Add("Type", GDALPDFObjectRW::CreateName("ExtGState"));
+            dfOpacity = CPLAtof(CPLGetXMLValue(
+                psBlending, "opacity", "1"));
+            gs.Add("ca", dfOpacity);
+            gs.Add("BM", GDALPDFObjectRW::CreateName(
+                CPLGetXMLValue(psBlending, "function", "Normal")));
+            VSIFPrintfL(m_fp, "%s\n", gs.Serialize().c_str());
+        }
+        EndObj();
+        oPageContext.m_oExtGState[
+            CPLOPrintf("GS%d", nExtGState.toInt())] = nExtGState;
+        oPageContext.m_osDrawingStream += "q\n";
+        oPageContext.m_osDrawingStream +=
+            CPLOPrintf("/GS%d gs\n", nExtGState.toInt());
+    }
+}
+
+/************************************************************************/
+/*                          EndBlending()                             */
+/************************************************************************/
+
+void GDALPDFComposerWriter::EndBlending(const CPLXMLNode* psNode,
+                                        PageContext& oPageContext)
+{
+    const auto psBlending = CPLGetXMLNode(psNode, "Blending");
+    if( psBlending )
+    {
+        oPageContext.m_osDrawingStream += "Q\n";
+    }
+}
+
+/************************************************************************/
+/*                           WriteRaster()                              */
+/************************************************************************/
+
+bool GDALPDFComposerWriter::WriteRaster(const CPLXMLNode* psNode,
+                                        PageContext& oPageContext)
+{
+    const char* pszDataset = CPLGetXMLValue(psNode, "dataset", nullptr);
+    if( !pszDataset )
+    {
+        CPLError(CE_Failure, CPLE_AppDefined,
+                "Missing dataset");
+        return false;
+    }
+    double dfX1 = CPLAtof(CPLGetXMLValue(psNode, "x1", "0"));
+    double dfY1 = CPLAtof(CPLGetXMLValue(psNode, "y1", "0"));
+    double dfX2 = CPLAtof(CPLGetXMLValue(psNode, "x2",
+        CPLSPrintf("%.18g", oPageContext.m_dfWidthInUserUnit)));
+    double dfY2 = CPLAtof(CPLGetXMLValue(psNode, "y2",
+        CPLSPrintf("%.18g", oPageContext.m_dfHeightInUserUnit)));
+    if( dfX2 <= dfX1 || dfY2 <= dfY1 )
+    {
+        CPLError(CE_Failure, CPLE_AppDefined, "Invalid x1,y1,x2,y2");
+        return false;
+    }
+    GDALDatasetUniquePtr poDS(GDALDataset::Open(
+        pszDataset, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
+        nullptr, nullptr, nullptr));
+    if( !poDS )
+        return false;
+    const int  nWidth = poDS->GetRasterXSize();
+    const int  nHeight = poDS->GetRasterYSize();
+    const int  nBlockXSize = std::max(16,
+        atoi(CPLGetXMLValue(psNode, "tileSize", "256")));
+    const int  nBlockYSize = nBlockXSize;
+    const char* pszCompressMethod = CPLGetXMLValue(
+        psNode, "Compression.method", "DEFLATE");
+    PDFCompressMethod eCompressMethod = COMPRESS_DEFLATE;
+    if( EQUAL(pszCompressMethod, "JPEG") )
+        eCompressMethod = COMPRESS_JPEG;
+    else if( EQUAL(pszCompressMethod, "JPEG2000") )
+        eCompressMethod = COMPRESS_JPEG2000;
+    const int nPredictor = CPLTestBool(CPLGetXMLValue(
+        psNode, "Compression.predictor", "false")) ? 2 : 0;
+    const int nJPEGQuality = atoi(
+        CPLGetXMLValue(psNode, "Compression.quality", "-1"));
+    const char* pszJPEG2000_DRIVER = m_osJPEG2000Driver.empty() ?
+        nullptr : m_osJPEG2000Driver.c_str();;
+
+    const char* pszGeoreferencingId =
+        CPLGetXMLValue(psNode, "georeferencingId", nullptr);
+    double dfClippingMinX = 0;
+    double dfClippingMinY = 0;
+    double dfClippingMaxX = 0;
+    double dfClippingMaxY = 0;
+    bool bClip = false;
+    double adfRasterGT[6] = {0,1,0,0,0,1};
+    double adfInvGeoreferencingGT[6]; // from georeferenced to PDF coordinates
+    if( pszGeoreferencingId )
+    {
+        auto iter = oPageContext.m_oMapGeoreferencedId.find(pszGeoreferencingId);
+        if( iter == oPageContext.m_oMapGeoreferencedId.end() )
+        {
+            CPLError(CE_Failure, CPLE_AppDefined,
+                     "Cannot find georeferencing of id %s",
+                     pszGeoreferencingId);
+            return false;
+        }
+        auto& georeferencing = iter->second;
+        dfX1 = georeferencing.m_bboxX1;
+        dfY1 = georeferencing.m_bboxY1;
+        dfX2 = georeferencing.m_bboxX2;
+        dfY2 = georeferencing.m_bboxY2;
+
+        bClip = true;
+        dfClippingMinX = APPLY_GT_X(georeferencing.m_adfGT, dfX1, dfY1);
+        dfClippingMinY = APPLY_GT_Y(georeferencing.m_adfGT, dfX1, dfY1);
+        dfClippingMaxX = APPLY_GT_X(georeferencing.m_adfGT, dfX2, dfY2);
+        dfClippingMaxY = APPLY_GT_Y(georeferencing.m_adfGT, dfX2, dfY2);
+
+        if( poDS->GetGeoTransform(adfRasterGT) != CE_None ||
+            adfRasterGT[2] != 0 || adfRasterGT[4] != 0 ||
+            adfRasterGT[5] > 0 )
+        {
+            CPLError(CE_Failure, CPLE_AppDefined,
+                     "Raster has no geotransform or a rotated geotransform");
+            return false;
+        }
+#if 1
+        const char* pWKT = poDS->GetProjectionRef();
+        if( !pWKT || pWKT[0] == '\0')
+        {
+            CPLError(CE_Failure, CPLE_AppDefined,
+                     "Raster has no geotransform or a rotated geotransform");
+            return false;
+        }
+        OGRSpatialReference *pSRS = new OGRSpatialReference();
+        if( pSRS->importFromWkt(pWKT) != OGRERR_NONE )
+        {
+            CPLError(CE_Failure, CPLE_AppDefined,
+                     "Raster has no geotransform or a rotated geotransform");
+            return false;
+        }
+        auto poSRS = pSRS;
+#else
+        // Since GDAL 3.0
+        auto poSRS = poDS->GetSpatialRef();
+#endif
+        if( !poSRS || !poSRS->IsSame(&georeferencing.m_oSRS) )
+        {
+            CPLError(CE_Failure, CPLE_AppDefined,
+                     "Raster has no projection, or different from the one "
+                     "of the georeferencing area");
+            return false;
+        }
+
+        CPL_IGNORE_RET_VAL(
+            GDALInvGeoTransform(georeferencing.m_adfGT,
+                                adfInvGeoreferencingGT));
+    }
+    const double dfRasterMinX = adfRasterGT[0];
+    const double dfRasterMaxY = adfRasterGT[3];
+
+    /* Does the source image has a color table ? */
+    const auto nColorTableId = WriteColorTable(poDS.get());
+
+    double dfIgnoredOpacity;
+    StartBlending(psNode, oPageContext, dfIgnoredOpacity);
+
+    CPLString osGroupStream;
+    std::vector<GDALPDFObjectNum> anImageIds;
+
+    const int nXBlocks = (nWidth + nBlockXSize - 1) / nBlockXSize;
+    const int nYBlocks = (nHeight + nBlockYSize - 1) / nBlockYSize;
+    int nBlockXOff, nBlockYOff;
+    for(nBlockYOff = 0; nBlockYOff < nYBlocks; nBlockYOff ++)
+    {
+        for(nBlockXOff = 0; nBlockXOff < nXBlocks; nBlockXOff ++)
+        {
+            int nReqWidth =
+                std::min(nBlockXSize, nWidth - nBlockXOff * nBlockXSize);
+            int nReqHeight =
+                std::min(nBlockYSize, nHeight - nBlockYOff * nBlockYSize);
+
+            int nX = nBlockXOff * nBlockXSize;
+            int nY = nBlockYOff * nBlockYSize;
+
+            double dfXPDFOff = nX * (dfX2 - dfX1) / nWidth + dfX1;
+            double dfYPDFOff = (nHeight - nY - nReqHeight) * (dfY2 - dfY1) / nHeight + dfY1;
+            double dfXPDFSize = nReqWidth * (dfX2 - dfX1) / nWidth;
+            double dfYPDFSize = nReqHeight * (dfY2 - dfY1) / nHeight;
+
+            if( bClip )
+            {
+                /* Compute extent of block to write */
+                double dfBlockMinX = adfRasterGT[0] + nX * adfRasterGT[1];
+                double dfBlockMaxX = adfRasterGT[0] + (nX + nReqWidth) * adfRasterGT[1];
+                double dfBlockMinY = adfRasterGT[3] + (nY + nReqHeight) * adfRasterGT[5];
+                double dfBlockMaxY = adfRasterGT[3] + nY * adfRasterGT[5];
+
+                // Clip the extent of the block with the extent of the main raster.
+                const double dfIntersectMinX =
+                    std::max(dfBlockMinX, dfClippingMinX);
+                const double dfIntersectMinY =
+                    std::max(dfBlockMinY, dfClippingMinY);
+                const double dfIntersectMaxX =
+                    std::min(dfBlockMaxX, dfClippingMaxX);
+                const double dfIntersectMaxY =
+                    std::min(dfBlockMaxY, dfClippingMaxY);
+
+                bool bOK = false;
+                if( dfIntersectMinX < dfIntersectMaxX &&
+                    dfIntersectMinY < dfIntersectMaxY )
+                {
+                    /* Re-compute (x,y,width,height) subwindow of current raster from */
+                    /* the extent of the clipped block */
+                    nX = (int)((dfIntersectMinX - dfRasterMinX) / adfRasterGT[1] + 0.5);
+                    nY = (int)((dfRasterMaxY - dfIntersectMaxY) / (-adfRasterGT[5]) + 0.5);
+                    nReqWidth = (int)((dfIntersectMaxX - dfRasterMinX) / adfRasterGT[1] + 0.5) - nX;
+                    nReqHeight = (int)((dfRasterMaxY - dfIntersectMinY) / (-adfRasterGT[5]) + 0.5) - nY;
+
+                    if( nReqWidth > 0 && nReqHeight > 0)
+                    {
+                        dfBlockMinX = adfRasterGT[0] + nX * adfRasterGT[1];
+                        dfBlockMaxX = adfRasterGT[0] + (nX + nReqWidth) * adfRasterGT[1];
+                        dfBlockMinY = adfRasterGT[3] + (nY + nReqHeight) * adfRasterGT[5];
+                        dfBlockMaxY = adfRasterGT[3] + nY * adfRasterGT[5];
+
+                        double dfPDFX1 = APPLY_GT_X(adfInvGeoreferencingGT, dfBlockMinX, dfBlockMinY);
+                        double dfPDFY1 = APPLY_GT_Y(adfInvGeoreferencingGT, dfBlockMinX, dfBlockMinY);
+                        double dfPDFX2 = APPLY_GT_X(adfInvGeoreferencingGT, dfBlockMaxX, dfBlockMaxY);
+                        double dfPDFY2 = APPLY_GT_Y(adfInvGeoreferencingGT, dfBlockMaxX, dfBlockMaxY);
+
+                        dfXPDFOff = dfPDFX1;
+                        dfYPDFOff = dfPDFY1;
+                        dfXPDFSize = dfPDFX2 - dfPDFX1;
+                        dfYPDFSize = dfPDFY2 - dfPDFY1;
+                        bOK = true;
+                    }
+                }
+                if( !bOK )
+                {
+                    continue;
+                }
+            }
+
+            const auto nImageId = WriteBlock(poDS.get(),
+                                    nX,
+                                    nY,
+                                    nReqWidth, nReqHeight,
+                                    nColorTableId,
+                                    eCompressMethod,
+                                    nPredictor,
+                                    nJPEGQuality,
+                                    pszJPEG2000_DRIVER,
+                                    nullptr,
+                                    nullptr);
+
+            if (!nImageId.toBool())
+                return false;
+
+            anImageIds.push_back(nImageId);
+            osGroupStream += "q\n";
+            GDALPDFObjectRW* poXSize = GDALPDFObjectRW::CreateReal(dfXPDFSize);
+            GDALPDFObjectRW* poYSize = GDALPDFObjectRW::CreateReal(dfYPDFSize);
+            GDALPDFObjectRW* poXOff = GDALPDFObjectRW::CreateReal(dfXPDFOff);
+            GDALPDFObjectRW* poYOff = GDALPDFObjectRW::CreateReal(dfYPDFOff);
+            osGroupStream += CPLOPrintf("%s 0 0 %s %s %s cm\n",
+                    poXSize->Serialize().c_str(),
+                    poYSize->Serialize().c_str(),
+                    poXOff->Serialize().c_str(),
+                    poYOff->Serialize().c_str());
+            delete poXSize;
+            delete poYSize;
+            delete poXOff;
+            delete poYOff;
+            osGroupStream += CPLOPrintf("/Image%d Do\n", nImageId.toInt());
+            osGroupStream += "Q\n";
+        }
+    }
+
+    if( anImageIds.size() <= 1 ||
+        CPLGetXMLNode(psNode, "Blending") == nullptr )
+    {
+        for( const auto& nImageId: anImageIds )
+        {
+            oPageContext.m_oXObjects[
+                    CPLOPrintf("Image%d", nImageId.toInt())] = nImageId;
+        }
+        oPageContext.m_osDrawingStream += osGroupStream;
+    }
+    else
+    {
+        // In case several tiles are drawn with blending, use a transparency
+        // group to avoid edge effects.
+
+        auto nGroupId = AllocNewObject();
+        GDALPDFDictionaryRW oDictGroup;
+        GDALPDFDictionaryRW* poGroup = new GDALPDFDictionaryRW();
+        poGroup->Add("Type", GDALPDFObjectRW::CreateName("Group"))
+                .Add("S",GDALPDFObjectRW::CreateName("Transparency"));
+
+        GDALPDFDictionaryRW* poXObjects = new GDALPDFDictionaryRW();
+        for( const auto& nImageId: anImageIds )
+        {
+            poXObjects->Add(CPLOPrintf("Image%d", nImageId.toInt()), nImageId, 0);
+        }
+        GDALPDFDictionaryRW* poResources = new GDALPDFDictionaryRW();
+        poResources->Add("XObject", poXObjects);
+
+        oDictGroup.Add("Type", GDALPDFObjectRW::CreateName("XObject"))
+            .Add("BBox", &((new GDALPDFArrayRW())
+                            ->Add(0).Add(0)).
+                              Add(oPageContext.m_dfWidthInUserUnit).
+                              Add(oPageContext.m_dfHeightInUserUnit))
+            .Add("Subtype", GDALPDFObjectRW::CreateName("Form"))
+            .Add("Group", poGroup)
+            .Add("Resources", poResources);
+
+
+        StartObjWithStream(nGroupId, oDictGroup,
+                            oPageContext.m_eStreamCompressMethod != COMPRESS_NONE);
+        VSIFPrintfL(m_fp, "%s", osGroupStream.c_str());
+        EndObjWithStream();
+
+        oPageContext.m_oXObjects[
+                    CPLOPrintf("Group%d", nGroupId.toInt())] = nGroupId;
+        oPageContext.m_osDrawingStream +=
+            CPLOPrintf("/Group%d Do\n", nGroupId.toInt());
+    }
+
+    EndBlending(psNode, oPageContext);
+
+    return true;
+}
+
+/************************************************************************/
+/*                     SetupVectorGeoreferencing()                      */
+/************************************************************************/
+
+bool GDALPDFComposerWriter::SetupVectorGeoreferencing(
+        const char* pszGeoreferencingId,
+        OGRLayer* poLayer,
+        const PageContext& oPageContext,
+        double& dfClippingMinX,
+        double& dfClippingMinY,
+        double& dfClippingMaxX,
+        double& dfClippingMaxY,
+        double adfMatrix[4],
+        std::unique_ptr<OGRCoordinateTransformation>& poCT)
+{
+    CPLAssert( pszGeoreferencingId );
+
+    auto iter = oPageContext.m_oMapGeoreferencedId.find(pszGeoreferencingId);
+    if( iter == oPageContext.m_oMapGeoreferencedId.end() )
+    {
+        CPLError(CE_Failure, CPLE_AppDefined,
+                    "Cannot find georeferencing of id %s",
+                    pszGeoreferencingId);
+        return false;
+    }
+    auto& georeferencing = iter->second;
+    const double dfX1 = georeferencing.m_bboxX1;
+    const double dfY1 = georeferencing.m_bboxY1;
+    const double dfX2 = georeferencing.m_bboxX2;
+    const double dfY2 = georeferencing.m_bboxY2;
+
+    dfClippingMinX = APPLY_GT_X(georeferencing.m_adfGT, dfX1, dfY1);
+    dfClippingMinY = APPLY_GT_Y(georeferencing.m_adfGT, dfX1, dfY1);
+    dfClippingMaxX = APPLY_GT_X(georeferencing.m_adfGT, dfX2, dfY2);
+    dfClippingMaxY = APPLY_GT_Y(georeferencing.m_adfGT, dfX2, dfY2);
+
+    auto poSRS = poLayer->GetSpatialRef();
+    if( !poSRS )
+    {
+        CPLError(CE_Failure, CPLE_AppDefined, "Layer has no SRS");
+        return false;
+    }
+    if( !poSRS->IsSame(&georeferencing.m_oSRS) )
+    {
+        poCT.reset(
+            OGRCreateCoordinateTransformation(poSRS, (OGRSpatialReference *)&georeferencing.m_oSRS));
+    }
+
+    if( !poCT )
+    {
+        poLayer->SetSpatialFilterRect(dfClippingMinX, dfClippingMinY,
+                                        dfClippingMaxX, dfClippingMaxY);
+    }
+
+    double adfInvGeoreferencingGT[6]; // from georeferenced to PDF coordinates
+    CPL_IGNORE_RET_VAL(
+        GDALInvGeoTransform(const_cast<double*>(georeferencing.m_adfGT),
+                            adfInvGeoreferencingGT));
+    adfMatrix[0] = adfInvGeoreferencingGT[0];
+    adfMatrix[1] = adfInvGeoreferencingGT[1];
+    adfMatrix[2] = adfInvGeoreferencingGT[3];
+    adfMatrix[3] = adfInvGeoreferencingGT[5];
+
+    return true;
+}
+
+/************************************************************************/
+/*                           WriteVector()                              */
+/************************************************************************/
+
+bool GDALPDFComposerWriter::WriteVector(const CPLXMLNode* psNode,
+                                        PageContext& oPageContext)
+{
+    const char* pszDataset = CPLGetXMLValue(psNode, "dataset", nullptr);
+    if( !pszDataset )
+    {
+        CPLError(CE_Failure, CPLE_AppDefined,
+                "Missing dataset");
+        return false;
+    }
+    const char* pszLayer = CPLGetXMLValue(psNode, "layer", nullptr);
+    if( !pszLayer )
+    {
+        CPLError(CE_Failure, CPLE_AppDefined,
+                "Missing layer");
+        return false;
+    }
+
+    GDALDatasetUniquePtr poDS(GDALDataset::Open(
+        pszDataset, GDAL_OF_VECTOR | GDAL_OF_VERBOSE_ERROR,
+        nullptr, nullptr, nullptr));
+    if( !poDS )
+        return false;
+    OGRLayer* poLayer = poDS->GetLayerByName(pszLayer);
+    if( !poLayer )
+    {
+        CPLError(CE_Failure, CPLE_AppDefined,
+                 "Cannt find layer %s", pszLayer);
+        return false;
+    }
+    const bool bVisible =
+        CPLTestBool(CPLGetXMLValue(psNode, "visible", "true"));
+
+    const auto psLogicalStructure =
+        CPLGetXMLNode(psNode, "LogicalStructure");
+    const char* pszOGRDisplayField = nullptr;
+    std::vector<CPLString> aosIncludedFields;
+    const bool bLogicalStructure = psLogicalStructure != nullptr;
+    if( psLogicalStructure )
+    {
+        pszOGRDisplayField = CPLGetXMLValue(psLogicalStructure,
+                                            "fieldToDisplay", nullptr);
+        if( CPLGetXMLNode(psLogicalStructure, "ExcludeAllFields") != nullptr ||
+            CPLGetXMLNode(psLogicalStructure, "IncludeField") != nullptr )
+        {
+            for(const auto* psIter = psLogicalStructure->psChild;
+                psIter; psIter = psIter->psNext)
+            {
+                if( psIter->eType == CXT_Element &&
+                    strcmp(psIter->pszValue, "IncludeField") == 0 )
+                {
+                    aosIncludedFields.push_back(CPLGetXMLValue(psIter, nullptr, ""));
+                }
+            }
+        }
+        else
+        {
+            std::set<CPLString> oSetExcludedFields;
+            for(const auto* psIter = psLogicalStructure->psChild;
+                psIter; psIter = psIter->psNext)
+            {
+                if( psIter->eType == CXT_Element &&
+                    strcmp(psIter->pszValue, "ExcludeField") == 0 )
+                {
+                    oSetExcludedFields.insert(CPLGetXMLValue(psIter, nullptr, ""));
+                }
+            }
+            const auto poLayerDefn = poLayer->GetLayerDefn();
+            for( int i = 0; i < poLayerDefn->GetFieldCount(); i++ )
+            {
+                const auto poFieldDefn = poLayerDefn->GetFieldDefn(i);
+                const char* pszName = poFieldDefn->GetNameRef();
+                if( oSetExcludedFields.find(pszName) == oSetExcludedFields.end() )
+                {
+                    aosIncludedFields.push_back(pszName);
+                }
+            }
+        }
+    }
+    const char* pszStyleString = CPLGetXMLValue(psNode,
+                                                    "ogrStyleString", nullptr);
+    const char* pszOGRLinkField = CPLGetXMLValue(psNode,
+                                                    "linkAttribute", nullptr);
+
+    const char* pszGeoreferencingId =
+        CPLGetXMLValue(psNode, "georeferencingId", nullptr);
+    std::unique_ptr<OGRCoordinateTransformation> poCT;
+    double dfClippingMinX = 0;
+    double dfClippingMinY = 0;
+    double dfClippingMaxX = 0;
+    double dfClippingMaxY = 0;
+    double adfMatrix[4] = { 0, 1, 0, 1 };
+    if( pszGeoreferencingId &&
+        !SetupVectorGeoreferencing(pszGeoreferencingId,
+                                   poLayer, oPageContext,
+                                   dfClippingMinX, dfClippingMinY,
+                                   dfClippingMaxX, dfClippingMaxY,
+                                   adfMatrix, poCT) )
+    {
+        return false;
+    }
+
+    double dfOpacityFactor = 1.0;
+    if( !bVisible )
+    {
+        if( oPageContext.m_oExtGState.find("GSinvisible") ==
+                oPageContext.m_oExtGState.end() )
+        {
+            auto nExtGState = AllocNewObject();
+            StartObj(nExtGState);
+            {
+                GDALPDFDictionaryRW gs;
+                gs.Add("Type", GDALPDFObjectRW::CreateName("ExtGState"));
+                gs.Add("ca", 0);
+                gs.Add("CA", 0);
+                VSIFPrintfL(m_fp, "%s\n", gs.Serialize().c_str());
+            }
+            EndObj();
+            oPageContext.m_oExtGState["GSinvisible"] = nExtGState;
+        }
+        oPageContext.m_osDrawingStream += "q\n";
+        oPageContext.m_osDrawingStream += "/GSinvisible gs\n";
+        oPageContext.m_osDrawingStream += "0 w\n";
+        dfOpacityFactor = 0;
+    }
+    else
+    {
+        StartBlending(psNode, oPageContext, dfOpacityFactor);
+    }
+
+    if (!m_nStructTreeRootId.toBool())
+        m_nStructTreeRootId = AllocNewObject();
+
+    GDALPDFObjectNum nFeatureLayerId;
+    if( bLogicalStructure )
+    {
+        nFeatureLayerId = AllocNewObject();
+        m_anFeatureLayerId.push_back(nFeatureLayerId);
+    }
+
+    std::vector<GDALPDFObjectNum> anFeatureUserProperties;
+    for( auto&& poFeature: poLayer )
+    {
+        auto hFeat = OGRFeature::ToHandle(poFeature.get());
+        auto hGeom = OGR_F_GetGeometryRef(hFeat);
+        if( !hGeom || OGR_G_IsEmpty(hGeom) )
+            continue;
+        if( poCT )
+        {
+            if( OGRGeometry::FromHandle(hGeom)->transform(poCT.get()) != OGRERR_NONE )
+                continue;
+
+            OGREnvelope sEnvelope;
+            OGR_G_GetEnvelope(hGeom, &sEnvelope);
+            if( sEnvelope.MinX > dfClippingMaxX ||
+                sEnvelope.MaxX < dfClippingMinX ||
+                sEnvelope.MinY > dfClippingMaxY ||
+                sEnvelope.MaxY < dfClippingMinY )
+            {
+                continue;
+            }
+        }
+
+        if( bLogicalStructure )
+        {
+            CPLString osOutFeatureName;
+            anFeatureUserProperties.push_back(
+                WriteAttributes(
+                            hFeat,
+                            aosIncludedFields,
+                            pszOGRDisplayField,
+                            oPageContext.m_nMCID,
+                            nFeatureLayerId,
+                            m_asPageId.back(),
+                            osOutFeatureName));
+        }
+
+        ObjectStyle os;
+        GetObjectStyle(pszStyleString, hFeat, adfMatrix,
+                       m_oMapSymbolFilenameToDesc, os);
+        os.nPenA = static_cast<int>(std::round(os.nPenA * dfOpacityFactor));
+        os.nBrushA = static_cast<int>(std::round(os.nBrushA * dfOpacityFactor));
+
+        const double dfRadius = os.dfSymbolSize;
+
+        if( os.nImageSymbolId.toBool() )
+        {
+            oPageContext.m_oXObjects[
+                CPLOPrintf("SymImage%d", os.nImageSymbolId.toInt())] = os.nImageSymbolId;
+        }
+
+        if( pszOGRLinkField )
+        {
+            OGREnvelope sEnvelope;
+            OGR_G_GetEnvelope(hGeom, &sEnvelope);
+            int bboxXMin, bboxYMin, bboxXMax, bboxYMax;
+            ComputeIntBBox(hGeom, sEnvelope, adfMatrix, os, dfRadius,
+                        bboxXMin, bboxYMin, bboxXMax, bboxYMax);
+
+            auto nLinkId = WriteLink(hFeat, pszOGRLinkField, adfMatrix,
+                    bboxXMin, bboxYMin, bboxXMax, bboxYMax);
+            if( nLinkId.toBool() )
+                oPageContext.m_anAnnotationsId.push_back(nLinkId);
+        }
+
+        if( bLogicalStructure )
+        {
+            oPageContext.m_osDrawingStream +=
+                CPLOPrintf("/feature <</MCID %d>> BDC\n", oPageContext.m_nMCID);
+        }
+
+        if( bVisible || bLogicalStructure )
+        {
+            oPageContext.m_osDrawingStream += "q\n";
+            if (bVisible && (os.nPenA != 255 || os.nBrushA != 255))
+            {
+                CPLString osGSName;
+                osGSName.Printf("GS_CA_%d_ca_%d", os.nPenA, os.nBrushA);
+                if( oPageContext.m_oExtGState.find(osGSName) ==
+                    oPageContext.m_oExtGState.end() )
+                {
+                    auto nExtGState = AllocNewObject();
+                    StartObj(nExtGState);
+                    {
+                        GDALPDFDictionaryRW gs;
+                        gs.Add("Type", GDALPDFObjectRW::CreateName("ExtGState"));
+                        if (os.nPenA != 255)
+                            gs.Add("CA", (os.nPenA == 127 || os.nPenA == 128) ? 0.5 : os.nPenA / 255.0);
+                        if (os.nBrushA != 255)
+                            gs.Add("ca", (os.nBrushA == 127 || os.nBrushA == 128) ? 0.5 : os.nBrushA / 255.0 );
+                        VSIFPrintfL(m_fp, "%s\n", gs.Serialize().c_str());
+                    }
+                    EndObj();
+                    oPageContext.m_oExtGState[osGSName] = nExtGState;
+                }
+                oPageContext.m_osDrawingStream += "/" + osGSName +" gs\n";
+            }
+
+            oPageContext.m_osDrawingStream +=
+                GenerateDrawingStream(hGeom, adfMatrix, os, dfRadius);
+
+            oPageContext.m_osDrawingStream += "Q\n";
+        }
+
+        if( bLogicalStructure )
+        {
+            oPageContext.m_osDrawingStream += "EMC\n";
+            oPageContext.m_nMCID ++;
+        }
+    }
+
+    if( bLogicalStructure )
+    {
+        for( const auto& num: anFeatureUserProperties )
+        {
+            oPageContext.m_anFeatureUserProperties.push_back(num);
+        }
+
+        {
+            StartObj(nFeatureLayerId);
+
+            GDALPDFDictionaryRW oDict;
+            GDALPDFDictionaryRW* poDictA = new GDALPDFDictionaryRW();
+            oDict.Add("A", poDictA);
+            poDictA->Add("O", GDALPDFObjectRW::CreateName("UserProperties"));
+            GDALPDFArrayRW* poArrayK = new GDALPDFArrayRW();
+            for( const auto& num: anFeatureUserProperties )
+                poArrayK->Add(num, 0);
+            oDict.Add("K", poArrayK);
+            oDict.Add("P", m_nStructTreeRootId, 0);
+            oDict.Add("S", GDALPDFObjectRW::CreateName("Layer"));
+
+            const char* pszOGRDisplayName =
+                CPLGetXMLValue(psLogicalStructure, "displayLayerName", poLayer->GetName());
+            oDict.Add("T", pszOGRDisplayName);
+
+            VSIFPrintfL(m_fp, "%s\n", oDict.Serialize().c_str());
+
+            EndObj();
+        }
+    }
+
+    if( !bVisible )
+    {
+        oPageContext.m_osDrawingStream += "Q\n";
+    }
+    else
+    {
+        EndBlending(psNode, oPageContext);
+    }
+
+    return true;
+}
+
+/************************************************************************/
+/*                         WriteVectorLabel()                           */
+/************************************************************************/
+
+bool GDALPDFComposerWriter::WriteVectorLabel(const CPLXMLNode* psNode,
+                                        PageContext& oPageContext)
+{
+    const char* pszDataset = CPLGetXMLValue(psNode, "dataset", nullptr);
+    if( !pszDataset )
+    {
+        CPLError(CE_Failure, CPLE_AppDefined,
+                "Missing dataset");
+        return false;
+    }
+    const char* pszLayer = CPLGetXMLValue(psNode, "layer", nullptr);
+    if( !pszLayer )
+    {
+        CPLError(CE_Failure, CPLE_AppDefined,
+                "Missing layer");
+        return false;
+    }
+
+    GDALDatasetUniquePtr poDS(GDALDataset::Open(
+        pszDataset, GDAL_OF_VECTOR | GDAL_OF_VERBOSE_ERROR,
+        nullptr, nullptr, nullptr));
+    if( !poDS )
+        return false;
+    OGRLayer* poLayer = poDS->GetLayerByName(pszLayer);
+    if( !poLayer )
+    {
+        CPLError(CE_Failure, CPLE_AppDefined,
+                 "Cannt find layer %s", pszLayer);
+        return false;
+    }
+
+    const char* pszStyleString = CPLGetXMLValue(psNode,
+                                                "ogrStyleString", nullptr);
+
+    double dfOpacityFactor = 1.0;
+    StartBlending(psNode, oPageContext, dfOpacityFactor);
+
+    const char* pszGeoreferencingId =
+        CPLGetXMLValue(psNode, "georeferencingId", nullptr);
+    std::unique_ptr<OGRCoordinateTransformation> poCT;
+    double dfClippingMinX = 0;
+    double dfClippingMinY = 0;
+    double dfClippingMaxX = 0;
+    double dfClippingMaxY = 0;
+    double adfMatrix[4] = { 0, 1, 0, 1 };
+    if( pszGeoreferencingId &&
+        !SetupVectorGeoreferencing(pszGeoreferencingId,
+                                   poLayer, oPageContext,
+                                   dfClippingMinX, dfClippingMinY,
+                                   dfClippingMaxX, dfClippingMaxY,
+                                   adfMatrix, poCT) )
+    {
+        return false;
+    }
+
+    for( auto&& poFeature: poLayer )
+    {
+        auto hFeat = OGRFeature::ToHandle(poFeature.get());
+        auto hGeom = OGR_F_GetGeometryRef(hFeat);
+        if( !hGeom || OGR_G_IsEmpty(hGeom) )
+            continue;
+        if( poCT )
+        {
+            if( OGRGeometry::FromHandle(hGeom)->transform(poCT.get()) != OGRERR_NONE )
+                continue;
+
+            OGREnvelope sEnvelope;
+            OGR_G_GetEnvelope(hGeom, &sEnvelope);
+            if( sEnvelope.MinX > dfClippingMaxX ||
+                sEnvelope.MaxX < dfClippingMinX ||
+                sEnvelope.MinY > dfClippingMaxY ||
+                sEnvelope.MaxY < dfClippingMinY )
+            {
+                continue;
+            }
+        }
+
+        ObjectStyle os;
+        GetObjectStyle(pszStyleString, hFeat, adfMatrix,
+                       m_oMapSymbolFilenameToDesc, os);
+        os.nPenA = static_cast<int>(std::round(os.nPenA * dfOpacityFactor));
+        os.nBrushA = static_cast<int>(std::round(os.nBrushA * dfOpacityFactor));
+
+        if (!os.osLabelText.empty() &&
+            wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint)
+        {
+            auto nObjectId = WriteLabel(hGeom, adfMatrix, os,
+                                        oPageContext.m_eStreamCompressMethod,
+                                        0,0,
+                                        oPageContext.m_dfWidthInUserUnit,
+                                        oPageContext.m_dfHeightInUserUnit);
+            oPageContext.m_osDrawingStream += 
+                CPLOPrintf("/Label%d Do\n", nObjectId.toInt());
+            oPageContext.m_oXObjects[
+                    CPLOPrintf("Label%d", nObjectId.toInt())] = nObjectId;
+        }
+    }
+
+    EndBlending(psNode, oPageContext);
+
+    return true;
+}
+
+#ifdef HAVE_PDF_READ_SUPPORT
+
+/************************************************************************/
+/*                            EmitNewObject()                           */
+/************************************************************************/
+
+GDALPDFObjectNum GDALPDFComposerWriter::EmitNewObject(GDALPDFObject* poObj,
+                                                      RemapType& oRemapObjectRefs)
+{
+    auto nId = AllocNewObject();
+    const auto nRefNum = poObj->GetRefNum();
+    if( nRefNum.toBool() )
+    {
+        int nRefGen = poObj->GetRefGen();
+        std::pair<int, int> oKey(nRefNum.toInt(), nRefGen);
+        oRemapObjectRefs[oKey] = nId;
+    }
+    CPLString osStr;
+    if( !SerializeAndRenumberIgnoreRef(osStr, poObj, oRemapObjectRefs) )
+        return GDALPDFObjectNum();
+    StartObj(nId);
+    VSIFWriteL(osStr.data(), 1, osStr.size(), m_fp);
+    VSIFPrintfL(m_fp, "\n");
+    EndObj();
+    return nId;
+}
+
+/************************************************************************/
+/*                         SerializeAndRenumber()                       */
+/************************************************************************/
+
+bool GDALPDFComposerWriter::SerializeAndRenumber(CPLString& osStr,
+                                                 GDALPDFObject* poObj,
+                                                 RemapType& oRemapObjectRefs)
+{
+    auto nRefNum = poObj->GetRefNum();
+    if( nRefNum.toBool() )
+    {
+        int nRefGen = poObj->GetRefGen();
+
+        std::pair<int, int> oKey(nRefNum.toInt(), nRefGen);
+        auto oIter = oRemapObjectRefs.find(oKey);
+        if( oIter != oRemapObjectRefs.end() )
+        {
+            osStr.append(CPLSPrintf("%d 0 R", oIter->second.toInt()));
+            return true;
+        }
+        else
+        {
+            auto nId = EmitNewObject(poObj, oRemapObjectRefs);
+            osStr.append(CPLSPrintf("%d 0 R", nId.toInt()));
+            return nId.toBool();
+        }
+    }
+    else
+    {
+        return SerializeAndRenumberIgnoreRef(osStr, poObj, oRemapObjectRefs);
+    }
+}
+
+/************************************************************************/
+/*                    SerializeAndRenumberIgnoreRef()                   */
+/************************************************************************/
+
+bool GDALPDFComposerWriter::SerializeAndRenumberIgnoreRef(CPLString& osStr,
+                                                 GDALPDFObject* poObj,
+                                                 RemapType& oRemapObjectRefs)
+{
+    switch(poObj->GetType())
+    {
+        case PDFObjectType_Array:
+        {
+            auto poArray = poObj->GetArray();
+            int nLength = poArray->GetLength();
+            osStr.append("[ ");
+            for(int i=0;i<nLength;i++)
+            {
+                if( !SerializeAndRenumber(osStr, poArray->Get(i), oRemapObjectRefs) )
+                    return false;
+                osStr.append(" ");
+            }
+            osStr.append("]");
+            break;
+        }
+        case PDFObjectType_Dictionary:
+        {
+            osStr.append("<< ");
+            auto poDict = poObj->GetDictionary();
+            auto& oMap = poDict->GetValues();
+            for( const auto& oIter: oMap )
+            {
+                const char* pszKey = oIter.first.c_str();
+                GDALPDFObject* poSubObj = oIter.second;
+                osStr.append("/");
+                osStr.append(pszKey);
+                osStr.append(" ");
+                if( !SerializeAndRenumber(osStr, poSubObj, oRemapObjectRefs) )
+                    return false;
+                osStr.append(" ");
+            }
+            osStr.append(">>");
+            auto poStream = poObj->GetStream();
+            if( poStream )
+            {
+                // CPLAssert( poObj->GetRefNum().toBool() ); // should be a top level object
+                osStr.append("\nstream\n");
+                auto pRawBytes = poStream->GetRawBytes();
+                if( !pRawBytes )
+                {
+                    CPLError(CE_Failure, CPLE_AppDefined,
+                             "Cannot get stream content");
+                    return false;
+                }
+                osStr.append(pRawBytes, poStream->GetRawLength());
+                VSIFree(pRawBytes);
+                osStr.append("\nendstream\n");
+            }
+            break;
+        }
+        case PDFObjectType_Unknown:
+        {
+            CPLError(CE_Failure, CPLE_AppDefined,
+                     "Corrupted PDF");
+            return false;
+        }
+        default:
+        {
+            poObj->Serialize(osStr, false);
+            break;
+        }
+    }
+    return true;
+}
+
+/************************************************************************/
+/*                         SerializeAndRenumber()                       */
+/************************************************************************/
+
+GDALPDFObjectNum GDALPDFComposerWriter::SerializeAndRenumber(GDALPDFObject* poObj)
+{
+    RemapType oRemapObjectRefs;
+    return EmitNewObject(poObj, oRemapObjectRefs);
+}
+
+/************************************************************************/
+/*                             WritePDF()                               */
+/************************************************************************/
+
+bool GDALPDFComposerWriter::WritePDF(const CPLXMLNode* psNode,
+                                        PageContext& oPageContext)
+{
+    const char* pszDataset = CPLGetXMLValue(psNode, "dataset", nullptr);
+    if( !pszDataset )
+    {
+        CPLError(CE_Failure, CPLE_AppDefined,
+                "Missing dataset");
+        return false;
+    }
+
+    GDALOpenInfo oOpenInfo(pszDataset, GA_ReadOnly);
+    std::unique_ptr<PDFDataset> poDS(PDFDataset::Open(&oOpenInfo));
+    if( !poDS )
+    {
+        CPLError(CE_Failure, CPLE_OpenFailed,
+                 "%s is not a valid PDF file", pszDataset);
+        return false;
+    }
+    if( poDS->GetPageWidth() != oPageContext.m_dfWidthInUserUnit ||
+        poDS->GetPageHeight() != oPageContext.m_dfHeightInUserUnit )
+    {
+        CPLError(CE_Warning, CPLE_AppDefined,
+                 "Dimensions of the inserted PDF page are %fx%f, which is "
+                 "different from the output PDF page %fx%f",
+                 poDS->GetPageWidth(),
+                 poDS->GetPageHeight(),
+                 oPageContext.m_dfWidthInUserUnit,
+                 oPageContext.m_dfHeightInUserUnit);
+    }
+    auto poPageObj = poDS->GetPageObj();
+    if( !poPageObj )
+        return false;
+    auto poPageDict = poPageObj->GetDictionary();
+    if( !poPageDict )
+        return false;
+    auto poContents = poPageDict->Get("Contents");
+    if (poContents != nullptr && poContents->GetType() == PDFObjectType_Array)
+    {
+        GDALPDFArray* poContentsArray = poContents->GetArray();
+        if (poContentsArray->GetLength() == 1)
+        {
+            poContents = poContentsArray->Get(0);
+        }
+    }
+    if (poContents == nullptr ||
+            poContents->GetType() != PDFObjectType_Dictionary )
+    {
+        CPLError(CE_Failure, CPLE_AppDefined, "Missing Contents");
+        return false;
+    }
+
+    auto poResources = poPageDict->Get("Resources");
+    if( !poResources )
+    {
+        CPLError(CE_Failure, CPLE_AppDefined, "Missing Resources");
+        return false;
+    }
+
+    // Serialize and renumber the Page Resources dictionary
+    auto nClonedResources = SerializeAndRenumber(poResources);
+    if( !nClonedResources.toBool() )
+    {
+        return false;
+    }
+
+    // Create a Transparency group using cloned Page Resources, and
+    // the Page Contents stream
+    auto nFormId = AllocNewObject();
+    GDALPDFDictionaryRW oDictGroup;
+    GDALPDFDictionaryRW* poGroup = new GDALPDFDictionaryRW();
+    poGroup->Add("Type", GDALPDFObjectRW::CreateName("Group"))
+            .Add("S",GDALPDFObjectRW::CreateName("Transparency"));
+
+    oDictGroup.Add("Type", GDALPDFObjectRW::CreateName("XObject"))
+        .Add("BBox", &((new GDALPDFArrayRW())
+                        ->Add(0).Add(0)).
+                            Add(oPageContext.m_dfWidthInUserUnit).
+                            Add(oPageContext.m_dfHeightInUserUnit))
+        .Add("Subtype", GDALPDFObjectRW::CreateName("Form"))
+        .Add("Group", poGroup)
+        .Add("Resources", nClonedResources, 0);
+
+    auto poStream = poContents->GetStream();
+    if( !poStream )
+    {
+        CPLError(CE_Failure, CPLE_AppDefined, "Missing Contents stream");
+        return false;
+    }
+    auto pabyContents = poStream->GetBytes();
+    if( !pabyContents )
+    {
+        return false;
+    }
+    const auto nContentsLength = poStream->GetLength();
+
+    StartObjWithStream(nFormId, oDictGroup,
+                       oPageContext.m_eStreamCompressMethod != COMPRESS_NONE);
+    VSIFWriteL(pabyContents, 1, nContentsLength, m_fp);
+    VSIFree(pabyContents);
+    EndObjWithStream();
+
+    // Paint the transparency group
+    double dfIgnoredOpacity;
+    StartBlending(psNode, oPageContext, dfIgnoredOpacity);
+
+    oPageContext.m_osDrawingStream += 
+                CPLOPrintf("/Form%d Do\n", nFormId.toInt());
+    oPageContext.m_oXObjects[
+            CPLOPrintf("Form%d", nFormId.toInt())] = nFormId;
+
+    EndBlending(psNode, oPageContext);
+
+    return true;
+}
+
+#endif // HAVE_PDF_READ_SUPPORT
+
+/************************************************************************/
+/*                              Generate()                              */
+/************************************************************************/
+
+bool GDALPDFComposerWriter::Generate(const CPLXMLNode* psComposition)
+{
+    m_osJPEG2000Driver = CPLGetXMLValue(psComposition, "JPEG2000Driver", "");
+
+    auto psMetadata = CPLGetXMLNode(psComposition, "Metadata");
+    if( psMetadata )
+    {
+        SetInfo(
+            CPLGetXMLValue(psMetadata, "Author", nullptr),
+            CPLGetXMLValue(psMetadata, "Producer", nullptr),
+            CPLGetXMLValue(psMetadata, "Creator", nullptr),
+            CPLGetXMLValue(psMetadata, "CreationDate", nullptr),
+            CPLGetXMLValue(psMetadata, "Subject", nullptr),
+            CPLGetXMLValue(psMetadata, "Title", nullptr),
+            CPLGetXMLValue(psMetadata, "Keywords", nullptr));
+        SetXMP(nullptr, CPLGetXMLValue(psMetadata, "XMP", nullptr));
+    }
+
+    const char* pszJavascript = CPLGetXMLValue(psComposition, "Javascript", nullptr);
+    if( pszJavascript )
+        WriteJavascript(pszJavascript, false);
+
+    auto psLayerTree = CPLGetXMLNode(psComposition, "LayerTree");
+    if( psLayerTree )
+    {
+        m_bDisplayLayersOnlyOnVisiblePages = CPLTestBool(
+            CPLGetXMLValue(psLayerTree, "displayOnlyOnVisiblePages", "false"));
+        if( !CreateLayerTree(psLayerTree, GDALPDFObjectNum(), &m_oTreeOfOGC) )
+            return false;
+    }
+
+    bool bFoundPage = false;
+    for(const auto* psIter = psComposition->psChild; psIter; psIter = psIter->psNext)
+    {
+        if( psIter->eType == CXT_Element &&
+            strcmp(psIter->pszValue, "Page") == 0 )
+        {
+            if( !GeneratePage(psIter) )
+                return false;
+            bFoundPage = true;
+        }
+    }
+    if( !bFoundPage )
+    {
+        CPLError(CE_Failure, CPLE_AppDefined,
+                 "At least one page should be defined");
+        return false;
+    }
+
+    auto psOutline = CPLGetXMLNode(psComposition, "Outline");
+    if( psOutline )
+    {
+        if( !CreateOutline(psOutline) )
+            return false;
+    }
+
+    return true;
+}
+
+
+/************************************************************************/
+/*                          GDALPDFErrorHandler()                       */
+/************************************************************************/
+
+static void CPL_STDCALL GDALPDFErrorHandler(CPL_UNUSED CPLErr eErr,
+                                           CPL_UNUSED CPLErrorNum nType,
+                                           const char *pszMsg)
+{
+    std::vector<CPLString> *paosErrors =
+        static_cast<std::vector<CPLString> *>(CPLGetErrorHandlerUserData());
+    paosErrors->push_back(pszMsg);
+}
+
+/************************************************************************/
+/*                      GDALPDFCreateFromCompositionFile()              */
+/************************************************************************/
+
+GDALDataset* GDALPDFCreateFromCompositionFile(const char* pszPDFFilename,
+                                              const char *pszXMLFilename)
+{
+    CPLXMLTreeCloser oXML(
+        (pszXMLFilename[0] == '<' &&
+            strstr(pszXMLFilename, "<PDFComposition") != nullptr) ?
+            CPLParseXMLString(pszXMLFilename) : CPLParseXMLFile(pszXMLFilename));
+    if( !oXML.get() )
+        return nullptr;
+    auto psComposition = CPLGetXMLNode(oXML.get(), "=PDFComposition");
+    if( !psComposition )
+    {
+        CPLError(CE_Failure, CPLE_AppDefined, "Cannot find PDFComposition");
+        return nullptr;
+    }
+
+    // XML Validation.
+    if( CPLTestBool(CPLGetConfigOption("GDAL_XML_VALIDATION", "YES")) )
+    {
+        const char *pszXSD = CPLFindFile("gdal", "pdfcomposition.xsd");
+        if( pszXSD != nullptr )
+        {
+            std::vector<CPLString> aosErrors;
+            CPLPushErrorHandlerEx(GDALPDFErrorHandler, &aosErrors);
+            const int bRet = CPLValidateXML(pszXMLFilename, pszXSD, nullptr);
+            CPLPopErrorHandler();
+            if( !bRet )
+            {
+                if( !aosErrors.empty() &&
+                    strstr(aosErrors[0].c_str(), "missing libxml2 support") ==
+                        nullptr )
+                {
+                    for( size_t i = 0; i < aosErrors.size(); i++ )
+                    {
+                        CPLError(CE_Warning, CPLE_AppDefined, "%s",
+                                 aosErrors[i].c_str());
+                    }
+                }
+            }
+            CPLErrorReset();
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create file.                                                    */
+/* -------------------------------------------------------------------- */
+    VSILFILE* fp = VSIFOpenL(pszPDFFilename, "wb");
+    if( fp == nullptr )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed,
+                  "Unable to create PDF file %s.\n",
+                  pszPDFFilename );
+        return nullptr;
+    }
+
+    GDALPDFComposerWriter oWriter(fp);
+    if( !oWriter.Generate(psComposition) )
+        return nullptr;
+
+    return new GDALFakePDFDataset();
+}
diff -urN gdal-2.3.2/frmts/pdf/pdfcreatefromcomposition.h gdal-2.3.2-pdf/frmts/pdf/pdfcreatefromcomposition.h
--- gdal-2.3.2/frmts/pdf/pdfcreatefromcomposition.h	1970-01-01 09:00:00.000000000 +0900
+++ gdal-2.3.2-pdf/frmts/pdf/pdfcreatefromcomposition.h	2019-12-12 12:44:04.921943906 +0900
@@ -0,0 +1,217 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  PDF driver
+ * Purpose:  GDALDataset driver for PDF dataset.
+ * Author:   Even Rouault, <even dot rouault at spatialys dot com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2019, Even Rouault <even dot rouault at spatialys dot com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#ifndef PDFCREATEFROMCOMPOSITION_H_INCLUDED
+#define PDFCREATEFROMCOMPOSITION_H_INCLUDED
+
+#include "gdal_pdf.h"
+#include "pdfcreatecopy.h"
+#include "cpl_minixml.h"
+#include "ogrsf_frmts.h"
+#include "ogr_geometry.h"
+
+#include <map>
+#include <memory>
+#include <vector>
+
+class GDALPDFComposerWriter final: public GDALPDFBaseWriter
+{
+        CPLString m_osJPEG2000Driver{};
+        struct TreeOfOCG
+        {
+            GDALPDFObjectNum                        m_nNum{};
+            bool                                    m_bInitiallyVisible{true};
+            std::vector<std::unique_ptr<TreeOfOCG>> m_children{};
+        };
+        bool m_bDisplayLayersOnlyOnVisiblePages = false;
+        TreeOfOCG m_oTreeOfOGC{};
+        std::map<CPLString, std::vector<GDALPDFObjectNum>> m_oMapExclusiveOCGIdToOCGs{};
+
+        std::map<CPLString, GDALPDFObjectNum> m_oMapLayerIdToOCG{};
+
+        struct xyPair
+        {
+            double x = 0;
+            double y = 0;
+            xyPair(double xin = 0.0, double yin = 0.0): x(xin), y(yin) {}
+        };
+
+        struct Georeferencing
+        {
+            CPLString           m_osID{};
+            OGRSpatialReference m_oSRS{};
+            double              m_bboxX1{};
+            double              m_bboxY1{};
+            double              m_bboxX2{};
+            double              m_bboxY2{};
+            double              m_adfGT[6]{0,1,0,0,0,1};
+        };
+
+        std::vector<GDALPDFObjectNum> m_anParentElements;
+        std::vector<GDALPDFObjectNum> m_anFeatureLayerId;
+        std::map<CPLString, GDALPDFObjectNum> m_oMapPageIdToObjectNum;
+        struct PageContext
+        {
+            double m_dfWidthInUserUnit = 0;
+            double m_dfHeightInUserUnit = 0;
+            CPLString m_osDrawingStream{};
+            std::vector<GDALPDFObjectNum> m_anFeatureUserProperties;
+            int m_nMCID = 0;
+            PDFCompressMethod m_eStreamCompressMethod = COMPRESS_DEFLATE;
+            std::map<CPLString, GDALPDFObjectNum> m_oXObjects{};
+            std::map<CPLString, GDALPDFObjectNum> m_oProperties{};
+            std::map<CPLString, GDALPDFObjectNum> m_oExtGState{};
+            std::vector<GDALPDFObjectNum> m_anAnnotationsId{};
+            std::map<CPLString, Georeferencing> m_oMapGeoreferencedId{};
+        };
+
+        bool CreateLayerTree(const CPLXMLNode* psNode,
+                             const GDALPDFObjectNum& nParentId,
+                             TreeOfOCG* parent);
+
+        struct Action
+        {
+            virtual ~Action() = default;
+        };
+
+        struct GotoPageAction final: public Action
+        {
+            GDALPDFObjectNum m_nPageDestId{};
+            double m_dfX1 = 0;
+            double m_dfX2 = 0;
+            double m_dfY1 = 0;
+            double m_dfY2 = 0;
+        };
+
+        struct SetLayerStateAction final: public Action
+        {
+            std::set<GDALPDFObjectNum> m_anONLayers{};
+            std::set<GDALPDFObjectNum> m_anOFFLayers{};
+        };
+
+        struct JavascriptAction final: public Action
+        {
+            CPLString m_osScript{};
+        };
+
+        bool ParseActions(const CPLXMLNode* psNode,
+                            std::vector<std::unique_ptr<Action>>& actions);
+        static GDALPDFDictionaryRW* SerializeActions(
+                        GDALPDFDictionaryRW* poDictForDest,
+                        const std::vector<std::unique_ptr<Action>>& actions);
+
+        struct OutlineItem
+        {
+            GDALPDFObjectNum m_nObjId{};
+            CPLString m_osName{};
+            bool m_bOpen = true;
+            int m_nFlags = 0;
+            std::vector<std::unique_ptr<Action>> m_aoActions{};
+            std::vector<std::unique_ptr<OutlineItem>> m_aoKids{};
+            int m_nKidsRecCount = 0;
+        };
+        GDALPDFObjectNum m_nOutlinesId{};
+
+        bool CreateOutlineFirstPass(const CPLXMLNode* psNode,
+                                    OutlineItem* poParentItem);
+        bool SerializeOutlineKids(const OutlineItem* poParentItem);
+        bool CreateOutline(const CPLXMLNode* psNode);
+
+        void WritePages();
+
+        static GDALPDFArrayRW* CreateOCGOrder(const TreeOfOCG* parent);
+        static void CollectOffOCG(std::vector<GDALPDFObjectNum>& ar,
+                                          const TreeOfOCG* parent);
+        bool GeneratePage(const CPLXMLNode* psPage);
+        bool GenerateGeoreferencing(const CPLXMLNode* psGeoreferencing,
+                                    double dfWidthInUserUnit,
+                                    double dfHeightInUserUnit,
+                                    GDALPDFObjectNum& nViewportId,
+                                    GDALPDFObjectNum& nLGIDictId,
+                                    Georeferencing& georeferencing);
+
+        GDALPDFObjectNum GenerateISO32000_Georeferencing(
+            OGRSpatialReferenceH hSRS,
+            double bboxX1, double bboxY1, double bboxX2, double bboxY2,
+            const std::vector<GDAL_GCP>& aGCPs,
+            const std::vector<xyPair>& aBoundingPolygon);
+
+        GDALPDFObjectNum GenerateOGC_BP_Georeferencing(
+            OGRSpatialReferenceH hSRS,
+            double bboxX1, double bboxY1, double bboxX2, double bboxY2,
+            const std::vector<GDAL_GCP>& aGCPs,
+            const std::vector<xyPair>& aBoundingPolygon);
+
+        bool ExploreContent(const CPLXMLNode* psNode, PageContext& oPageContext);
+        bool WriteRaster(const CPLXMLNode* psNode, PageContext& oPageContext);
+        bool WriteVector(const CPLXMLNode* psNode, PageContext& oPageContext);
+        bool WriteVectorLabel(const CPLXMLNode* psNode, PageContext& oPageContext);
+        void StartBlending(const CPLXMLNode* psNode, PageContext& oPageContext,
+                           double& dfOpacity);
+        static void EndBlending(const CPLXMLNode* psNode, PageContext& oPageContext);
+
+        static bool SetupVectorGeoreferencing(
+            const char* pszGeoreferencingId,
+            OGRLayer* poLayer,
+            const PageContext& oPageContext,
+            double& dfClippingMinX,
+            double& dfClippingMinY,
+            double& dfClippingMaxX,
+            double& dfClippingMaxY,
+            double adfMatrix[4],
+            std::unique_ptr<OGRCoordinateTransformation>& poCT);
+
+#ifdef HAVE_PDF_READ_SUPPORT
+        bool WritePDF(const CPLXMLNode* psNode, PageContext& oPageContext);
+
+        typedef std::map< std::pair<int, int>, GDALPDFObjectNum> RemapType;
+        GDALPDFObjectNum EmitNewObject(GDALPDFObject* poObj,
+                                       RemapType& oRemapObjectRefs);
+        GDALPDFObjectNum SerializeAndRenumber(GDALPDFObject* poObj);
+        bool SerializeAndRenumber(CPLString& osStr,
+            GDALPDFObject* poObj,
+            RemapType& oRemapObjectRefs);
+        bool SerializeAndRenumberIgnoreRef(CPLString& osStr,
+            GDALPDFObject* poObj,
+            RemapType& oRemapObjectRefs);
+#endif
+
+    public:
+        explicit GDALPDFComposerWriter(VSILFILE* fp);
+        ~GDALPDFComposerWriter();
+
+        bool Generate(const CPLXMLNode* psComposition);
+        void Close();
+};
+
+GDALDataset* GDALPDFCreateFromCompositionFile(const char* pszPDFFilename,
+                                              const char *pszXMLFilename);
+
+
+#endif // PDFCREATEFROMCOMPOSITION_H_INCLUDED
diff -urN gdal-2.3.2/frmts/pdf/pdfdataset.cpp gdal-2.3.2-pdf/frmts/pdf/pdfdataset.cpp
--- gdal-2.3.2/frmts/pdf/pdfdataset.cpp	2018-09-21 18:04:33.000000000 +0900
+++ gdal-2.3.2-pdf/frmts/pdf/pdfdataset.cpp	2019-12-12 14:01:35.296765800 +0900
@@ -2,7 +2,7 @@
  *
  * Project:  PDF driver
  * Purpose:  GDALDataset driver for PDF dataset.
- * Author:   Even Rouault, <even dot rouault at mines dash paris dot org>
+ * Author:   Even Rouault, <even dot rouault at spatialys.com>
  *
  ******************************************************************************
  *
@@ -12,7 +12,7 @@
  * Author: Martin Mikita <martin.mikita@klokantech.com>, xmikit00 @ FIT VUT Brno
  *
  ******************************************************************************
- * Copyright (c) 2010-2014, Even Rouault <even dot rouault at mines-paris dot org>
+ * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys.com>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -56,9 +56,9 @@
 
 /* g++ -fPIC -g -Wall frmts/pdf/pdfdataset.cpp -shared -o gdal_PDF.so -Iport -Igcore -Iogr -L. -lgdal -lpoppler -I/usr/include/poppler */
 
-CPL_CVSID("$Id: pdfdataset.cpp 22f8ae3bf7bc3cccd970992655c63fc5254d3206 2018-04-08 20:13:05 +0200 Even Rouault $")
+CPL_CVSID("$Id$")
 
-#if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) || defined(HAVE_PDFIUM)
+#ifdef HAVE_PDF_READ_SUPPORT
 
 #if defined(HAVE_PDFIUM) && defined(HAVE_POPPLER)
 #define HAVE_MULTIPLE_PDF_BACKENDS
@@ -130,7 +130,7 @@
 public:
     ObjectAutoFree() {}
     ~ObjectAutoFree() {
-#ifndef POPPLER_0_58_OR_LATER
+#if !(POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 58)
         obj.free();
 #endif
     }
@@ -176,17 +176,10 @@
         void SetEnableText(int bFlag) { bEnableText = bFlag; }
         void SetEnableBitmap(int bFlag) { bEnableBitmap = bFlag; }
 
-        virtual void startPage(int pageNum, GfxState *state
-#ifdef POPPLER_0_23_OR_LATER
-                               ,XRef* xrefIn
-#endif
+        virtual void startPage(int pageNum, GfxState *state,XRef* xrefIn
         ) override
         {
-            SplashOutputDev::startPage(pageNum, state
-#ifdef POPPLER_0_23_OR_LATER
-                                       ,xrefIn
-#endif
-            );
+            SplashOutputDev::startPage(pageNum, state,xrefIn);
             SplashBitmap* poBitmap = getBitmap();
             memset(poBitmap->getDataPtr(), 255, poBitmap->getRowSize() * poBitmap->getHeight());
         }
@@ -212,7 +205,11 @@
         virtual void drawChar(GfxState *state, double x, double y,
                               double dx, double dy,
                               double originX, double originY,
-                              CharCode code, int nBytes, Unicode *u, int uLen) override
+                              CharCode code, int nBytes,
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 82
+                              const
+#endif
+                              Unicode *u, int uLen) override
         {
             if (bEnableText)
                 SplashOutputDev::drawChar(state, x, y, dx, dy,
@@ -226,15 +223,6 @@
                 SplashOutputDev::beginTextObject(state);
         }
 
-#ifndef POPPLER_0_23_OR_LATER
-        virtual GBool deviceHasTextClip(GfxState *state) override
-        {
-            if (bEnableText)
-                return SplashOutputDev::deviceHasTextClip(state);
-            return gFalse;
-        }
-#endif
-
         virtual void endTextObject(GfxState *state) override
         {
             if (bEnableText)
@@ -260,7 +248,6 @@
             }
         }
 
-#ifdef POPPLER_0_20_OR_LATER
         virtual void setSoftMaskFromImageMask(GfxState *state,
                             Object *ref, Stream *str,
                             int width, int height, GBool invert,
@@ -279,11 +266,15 @@
             if (bEnableBitmap)
                 SplashOutputDev::unsetSoftMaskFromImageMask(state, baseMatrix);
         }
-#endif
 
         virtual void drawImage(GfxState *state, Object *ref, Stream *str,
                                int width, int height, GfxImageColorMap *colorMap,
-                               GBool interpolate, int *maskColors, GBool inlineImg) override
+                               GBool interpolate,
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 82
+                               const
+#endif
+                               int *maskColors,
+                               GBool inlineImg) override
         {
             if (bEnableBitmap)
                 SplashOutputDev::drawImage(state, ref, str,
@@ -469,7 +460,7 @@
         osIndent += " ";
     fprintf(f, "%sType = %s",
             osIndent.c_str(), poObj->GetTypeName());
-    int nRefNum = poObj->GetRefNum();
+    int nRefNum = poObj->GetRefNum().toInt();
     if (nRefNum != 0)
         fprintf(f, ", Num = %d, Gen = %d",
                 nRefNum, poObj->GetRefGen());
@@ -512,7 +503,8 @@
     GDALPDFStream* poStream = poObj->GetStream();
     if (poStream != nullptr)
     {
-        fprintf(f, "%sHas stream (%d bytes)\n", osIndent.c_str(), poStream->GetLength());
+        fprintf(f, "%sHas stream (%d uncompressed bytes, %d raw bytes)\n",
+                osIndent.c_str(), poStream->GetLength(), poStream->GetRawLength());
     }
 }
 
@@ -535,9 +527,9 @@
         GDALPDFObject* poObj = oIter->second;
         if (strcmp(pszKey, "Parent") == 0 && !bDumpParent)
         {
-            if (poObj->GetRefNum())
+            if (poObj->GetRefNum().toBool())
                 fprintf(f, ", Num = %d, Gen = %d",
-                        poObj->GetRefNum(), poObj->GetRefGen());
+                        poObj->GetRefNum().toInt(), poObj->GetRefGen());
             fprintf(f, "\n");
             continue;
         }
@@ -1094,7 +1086,7 @@
     psFileAccess->m_Param = fp;
     psFileAccess->m_FileLen = nFileLen;
     psFileAccess->m_GetBlock = GDALPdfiumGetBlock;
-    CPDF_Document* docPdfium = reinterpret_cast<CPDF_Document*>(
+    CPDF_Document* docPdfium = CPDFDocumentFromFPDFDocument(
         FPDF_LoadCustomDocument(psFileAccess, nullptr));
     if(docPdfium == nullptr)
     {
@@ -1102,7 +1094,7 @@
       if( err == FPDF_ERR_PASSWORD) {
           if(pszUserPwd) {
             pszUserPwd = PDFEnterPasswordFromConsoleIfNeeded(pszUserPwd);
-            docPdfium = reinterpret_cast<CPDF_Document*>(FPDF_LoadCustomDocument(psFileAccess, pszUserPwd));
+            docPdfium = CPDFDocumentFromFPDFDocument(FPDF_LoadCustomDocument(psFileAccess, pszUserPwd));
             if(docPdfium == nullptr)
               err = FPDF_GetLastError();
             else
@@ -1173,7 +1165,7 @@
   /* Sanity check to validate page count */
   if( pageNum != nPages )
   {
-      if( poDoc->doc->GetPage(nPages - 1) == nullptr )
+      if( poDoc->doc->GetPageDictionary(nPages - 1) == nullptr )
       {
         CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid page count");
         CPLReleaseMutex(g_oPdfiumLoadDocMutex);
@@ -1186,21 +1178,14 @@
   TPdfiumPageStruct *poPage = nullptr;
   // Page not loaded
   if(itPage == poDoc->pages.end()) {
-    CPDF_Dictionary* pDict = poDoc->doc->GetPage(pageNum - 1);
+    CPDF_Dictionary* pDict = poDoc->doc->GetPageDictionary(pageNum - 1);
     if (pDict == nullptr) {
       CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDFium : invalid page");
 
       CPLReleaseMutex(g_oPdfiumLoadDocMutex);
       return FALSE;
     }
-    CPDF_Page* pPage = new CPDF_Page;
-    if(!pPage) {
-      CPLError(CE_Failure, CPLE_AppDefined, "Not enough memory for Pdfium Page object");
-
-      CPLReleaseMutex(g_oPdfiumLoadDocMutex);
-      return FALSE;
-    }
-    pPage->Load(poDoc->doc, pDict);
+    auto pPage = pdfium::MakeRetain<CPDF_Page>(poDoc->doc, pDict);
 
     poPage = new TPdfiumPageStruct;
     if(!poPage) {
@@ -1210,7 +1195,7 @@
       return FALSE;
     }
     poPage->pageNum = pageNum;
-    poPage->page = pPage;
+    poPage->page = pPage.Leak();
     poPage->readMutex = nullptr;
     poPage->sharedNum = 0;
 
@@ -1267,7 +1252,7 @@
   CPLReleaseMutex(pPage->readMutex);
   CPLDestroyMutex(pPage->readMutex);
   // Close page and remove from map
-  FPDF_ClosePage(pPage->page);
+  FPDF_ClosePage(FPDFPageFromIPDFPage(pPage->page));
 
   pDoc->pages.erase(pPage->pageNum);
   delete pPage;
@@ -1284,7 +1269,7 @@
   }
 
   // Close document and remove from map
-  FPDF_CloseDocument(pDoc->doc);
+  FPDF_CloseDocument(FPDFDocumentFromCPDFDocument(pDoc->doc));
   g_mPdfiumDatasets.erase(pDoc->filename);
   CPLFree(pDoc->filename);
   VSIFCloseL((VSILFILE*)pDoc->psFileAccess->m_Param);
@@ -1366,25 +1351,25 @@
 /*                         GDALPDFiumOCContext                          */
 /************************************************************************/
 
-class GDALPDFiumOCContext : public IPDF_OCContext
+class GDALPDFiumOCContext : public CPDF_OCContextInterface
 {
     PDFDataset* m_poDS;
-    CPDF_OCContext m_DefaultOCContext;
+    RetainPtr<CPDF_OCContext> m_DefaultOCContext;
 public:
 
-    GDALPDFiumOCContext(PDFDataset* poDS, CPDF_Document *pDoc) :
-                                m_poDS(poDS), m_DefaultOCContext(pDoc) {}
+    GDALPDFiumOCContext(PDFDataset* poDS, CPDF_Document *pDoc, CPDF_OCContext::UsageType usage) :
+                                m_poDS(poDS), m_DefaultOCContext(pdfium::MakeRetain<CPDF_OCContext>(pDoc, usage)) {}
 
-    virtual FX_BOOL CheckOCGVisible(const CPDF_Dictionary *pOCGDict) override
+    virtual bool CheckOCGVisible(const CPDF_Dictionary *pOCGDict) const override
     {
         PDFDataset::VisibilityState eVisibility =
             m_poDS->GetVisibilityStateForOGCPdfium(
                                 pOCGDict->GetObjNum(), pOCGDict->GetGenNum() );
         if( eVisibility == PDFDataset::VISIBILITY_ON )
-            return TRUE;
+            return true;
         if( eVisibility == PDFDataset::VISIBILITY_OFF )
-            return FALSE;
-        return m_DefaultOCContext.CheckOCGVisible(pOCGDict);
+            return false;
+        return m_DefaultOCContext->CheckOCGVisible(pOCGDict);
     }
 };
 
@@ -1392,9 +1377,9 @@
 /*                      GDALPDFiumRenderDeviceDriver                    */
 /************************************************************************/
 
-class GDALPDFiumRenderDeviceDriver: public IFX_RenderDeviceDriver
+class GDALPDFiumRenderDeviceDriver: public RenderDeviceDriverIface
 {
-        IFX_RenderDeviceDriver* m_poParent;
+        std::unique_ptr<RenderDeviceDriverIface> m_poParent;
         CFX_RenderDevice* m_pDevice;
 
         int bEnableVector;
@@ -1404,31 +1389,29 @@
 
 public:
 
-    GDALPDFiumRenderDeviceDriver(IFX_RenderDeviceDriver* poParent, CFX_RenderDevice* pDevice):
-                                                        m_poParent(poParent),
+    GDALPDFiumRenderDeviceDriver(std::unique_ptr<RenderDeviceDriverIface>&& poParent, CFX_RenderDevice* pDevice):
+                                                        m_poParent(std::move(poParent)),
                                                         m_pDevice(pDevice),
                                                         bEnableVector(TRUE),
                                                         bEnableText(TRUE),
                                                         bEnableBitmap(TRUE),
                                                         bTemporaryEnableVectorForTextStroking(FALSE) {}
-    virtual ~GDALPDFiumRenderDeviceDriver() { delete m_poParent; }
+    virtual ~GDALPDFiumRenderDeviceDriver() = default;
 
     void SetEnableVector(int bFlag) { bEnableVector = bFlag; }
     void SetEnableText(int bFlag) { bEnableText = bFlag; }
     void SetEnableBitmap(int bFlag) { bEnableBitmap = bFlag; }
 
-    virtual void Begin() override { m_poParent->Begin(); }
-    virtual void End() override { m_poParent->End(); }
-    virtual int         GetDeviceCaps(int caps_id) override { return m_poParent->GetDeviceCaps(caps_id); }
-    virtual CFX_Matrix  GetCTM() const override { return m_poParent->GetCTM(); }
-    virtual FX_BOOL IsPSPrintDriver() override { return m_poParent->IsPSPrintDriver(); }
-    virtual FX_BOOL     StartRendering() override { return m_poParent->StartRendering(); }
-    virtual void        EndRendering() override { m_poParent->EndRendering(); }
+    virtual DeviceType GetDeviceType() const override { return m_poParent->GetDeviceType(); }
+    virtual int         GetDeviceCaps(int caps_id) const override { return m_poParent->GetDeviceCaps(caps_id); }
+
+    virtual bool StartRendering() override { return m_poParent->StartRendering(); }
+    virtual void EndRendering() override { m_poParent->EndRendering(); }
     virtual void        SaveState() override { m_poParent->SaveState(); }
-    virtual void        RestoreState(FX_BOOL bKeepSaved = FALSE) override { m_poParent->RestoreState(bKeepSaved); }
+    virtual void        RestoreState(bool bKeepSaved) override { m_poParent->RestoreState(bKeepSaved); }
 
-    virtual FX_BOOL     SetClip_PathFill(const CFX_PathData* pPathData,
-                                     const CFX_AffineMatrix* pObject2Device,
+    virtual bool     SetClip_PathFill(const CFX_PathData* pPathData,
+                                     const CFX_Matrix* pObject2Device,
                                      int fill_mode
                                     ) override
     {
@@ -1437,8 +1420,8 @@
         return m_poParent->SetClip_PathFill(pPathData, pObject2Device, fill_mode);
     }
 
-    virtual FX_BOOL     SetClip_PathStroke(const CFX_PathData* pPathData,
-                                       const CFX_AffineMatrix* pObject2Device,
+    virtual bool     SetClip_PathStroke(const CFX_PathData* pPathData,
+                                       const CFX_Matrix* pObject2Device,
                                        const CFX_GraphStateData* pGraphState
                                       ) override
     {
@@ -1447,105 +1430,118 @@
         return m_poParent->SetClip_PathStroke(pPathData, pObject2Device, pGraphState);
     }
 
-    virtual FX_BOOL     DrawPath(const CFX_PathData* pPathData,
-                             const CFX_AffineMatrix* pObject2Device,
-                             const CFX_GraphStateData* pGraphState,
-                             FX_DWORD fill_color,
-                             FX_DWORD stroke_color,
-                             int fill_mode,
-                             int alpha_flag = 0,
-                             void* pIccTransform = nullptr,
-                             int blend_type = FXDIB_BLEND_NORMAL
-                            )  override
+    virtual bool     DrawPath(const CFX_PathData* pPathData,
+                        const CFX_Matrix* pObject2Device,
+                        const CFX_GraphStateData* pGraphState,
+                        uint32_t fill_color,
+                        uint32_t stroke_color,
+                        int fill_mode,
+                        BlendMode blend_type)  override
     {
         if( !bEnableVector && !bTemporaryEnableVectorForTextStroking )
             return TRUE;
         return m_poParent->DrawPath(pPathData, pObject2Device, pGraphState ,
                                     fill_color, stroke_color, fill_mode,
-                                    alpha_flag, pIccTransform, blend_type);
+                                    blend_type);
     }
 
-    virtual FX_BOOL     SetPixel(int x, int y, FX_DWORD color,
-                             int alpha_flag = 0, void* pIccTransform = nullptr) override
+    virtual bool     SetPixel(int x, int y, uint32_t color) override
     {
         if( !bEnableBitmap && !bTemporaryEnableVectorForTextStroking )
             return TRUE;
-        return m_poParent->SetPixel(x,y,color,alpha_flag,pIccTransform);
+        return m_poParent->SetPixel(x,y,color);
     }
 
-    virtual FX_BOOL FillRect(const FX_RECT* pRect, FX_DWORD fill_color,
-                             int alpha_flag = 0, void* pIccTransform = nullptr, int blend_type = FXDIB_BLEND_NORMAL) override
+    virtual bool FillRectWithBlend(const FX_RECT& rect,
+                                 uint32_t fill_color,
+                                 BlendMode blend_type) override
     {
-        return m_poParent->FillRect(pRect,fill_color,alpha_flag,pIccTransform,blend_type);
+        return m_poParent->FillRectWithBlend(rect,fill_color,blend_type);
     }
 
-    virtual FX_BOOL     DrawCosmeticLine(FX_FLOAT x1, FX_FLOAT y1, FX_FLOAT x2, FX_FLOAT y2, FX_DWORD color,
-                                     int alpha_flag = 0, void* pIccTransform = nullptr, int blend_type = FXDIB_BLEND_NORMAL) override
+    virtual bool     DrawCosmeticLine(const CFX_PointF& ptMoveTo,
+                                      const CFX_PointF& ptLineTo,
+                                      uint32_t color,
+                                      BlendMode blend_typeL) override
     {
         if( !bEnableVector && !bTemporaryEnableVectorForTextStroking )
             return TRUE;
-        return m_poParent->DrawCosmeticLine(x1,y1,x2,y2,color,alpha_flag,pIccTransform,blend_type);
+        return m_poParent->DrawCosmeticLine(ptMoveTo,ptLineTo,color,blend_typeL);
     }
 
-    virtual FX_BOOL GetClipBox(FX_RECT* pRect)  override
+    virtual bool GetClipBox(FX_RECT* pRect)  override
     {
         return m_poParent->GetClipBox(pRect);
     }
 
-    virtual FX_BOOL     GetDIBits(CFX_DIBitmap* pBitmap, int left, int top, void* pIccTransform = nullptr, FX_BOOL bDEdge = FALSE) override
+    virtual bool     GetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
+                         int left,
+                         int top) override
     {
-        return m_poParent->GetDIBits(pBitmap,left,top, pIccTransform, bDEdge);
+        return m_poParent->GetDIBits(pBitmap,left,top);
     }
-    virtual CFX_DIBitmap*   GetBackDrop() override
+
+    virtual RetainPtr<CFX_DIBitmap> GetBackDrop() override
     {
         return m_poParent->GetBackDrop();
     }
 
-    virtual FX_BOOL     SetDIBits(const CFX_DIBSource* pBitmap, FX_DWORD color, const FX_RECT* pSrcRect,
-                              int dest_left, int dest_top, int blend_type,
-                              int alpha_flag = 0, void* pIccTransform = nullptr) override
+    virtual bool     SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                               uint32_t color,
+                               const FX_RECT& src_rect,
+                               int dest_left,
+                               int dest_top,
+                               BlendMode blend_type) override
     {
         if( !bEnableBitmap && !bTemporaryEnableVectorForTextStroking )
-            return TRUE;
-        return m_poParent->SetDIBits(pBitmap, color, pSrcRect,
-                                     dest_left, dest_top, blend_type,
-                                     alpha_flag, pIccTransform);
+            return true;
+        return m_poParent->SetDIBits(pBitmap, color, src_rect,
+                                     dest_left, dest_top, blend_type);
     }
 
-    virtual FX_BOOL     StretchDIBits(const CFX_DIBSource* pBitmap, FX_DWORD color, int dest_left, int dest_top,
-                                  int dest_width, int dest_height, const FX_RECT* pClipRect, FX_DWORD flags,
-                                  int alpha_flag = 0, void* pIccTransform = nullptr, int blend_type = FXDIB_BLEND_NORMAL) override
+    virtual bool     StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                             uint32_t color,
+                             int dest_left,
+                             int dest_top,
+                             int dest_width,
+                             int dest_height,
+                             const FX_RECT* pClipRect,
+                             const FXDIB_ResampleOptions& options,
+                             BlendMode blend_type) override
     {
         if( !bEnableBitmap && !bTemporaryEnableVectorForTextStroking )
-            return TRUE;
+            return true;
         return m_poParent->StretchDIBits(pBitmap, color, dest_left, dest_top,
-                                     dest_width, dest_height, pClipRect, flags,
-                                     alpha_flag, pIccTransform, blend_type);
+                                     dest_width, dest_height, pClipRect,
+                                     options, blend_type);
     }
 
-    virtual FX_BOOL     StartDIBits(const CFX_DIBSource* pBitmap, int bitmap_alpha, FX_DWORD color,
-                                const CFX_AffineMatrix* pMatrix, FX_DWORD flags, void*& handle,
-                                int alpha_flag = 0, void* pIccTransform = nullptr, int blend_type = FXDIB_BLEND_NORMAL) override
+    virtual bool     StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                           int bitmap_alpha,
+                           uint32_t color,
+                           const CFX_Matrix& matrix,
+                           const FXDIB_ResampleOptions& options,
+                           std::unique_ptr<CFX_ImageRenderer>* handle,
+                           BlendMode blend_type) override
     {
         if( !bEnableBitmap && !bTemporaryEnableVectorForTextStroking )
-            return TRUE;
-        return m_poParent->StartDIBits(pBitmap, bitmap_alpha, color, pMatrix, flags,
-                                       handle, alpha_flag, pIccTransform, blend_type);
+            return true;
+        return m_poParent->StartDIBits(pBitmap, bitmap_alpha, color, matrix, options,
+                                       handle, blend_type);
     }
 
-    virtual FX_BOOL     ContinueDIBits(void* handle, IFX_Pause* pPause) override
+    virtual bool     ContinueDIBits(CFX_ImageRenderer* handle,
+                                    PauseIndicatorIface* pPause) override
     {
         return m_poParent->ContinueDIBits(handle, pPause);
     }
 
-    virtual void        CancelDIBits(void* handle) override
-    {
-        m_poParent->CancelDIBits(handle);
-    }
-
-    virtual FX_BOOL DrawDeviceText(int nChars, const FXTEXT_CHARPOS* pCharPos, CFX_Font* pFont,
-                                   CFX_FontCache* pCache, const CFX_AffineMatrix* pObject2Device, FX_FLOAT font_size, FX_DWORD color,
-                                   int alpha_flag = 0, void* pIccTransform = nullptr) override
+    virtual bool DrawDeviceText(int nChars,
+                              const TextCharPos* pCharPos,
+                              CFX_Font* pFont,
+                              const CFX_Matrix& mtObject2Device,
+                              float font_size,
+                              uint32_t color) override
     {
         if( bEnableText )
         {
@@ -1554,41 +1550,58 @@
             // that the rendering will happen in the next phase
             if( bTemporaryEnableVectorForTextStroking )
                 return FALSE; // this is the default behaviour of the parent
-            bTemporaryEnableVectorForTextStroking = TRUE;
-            FX_BOOL bRet = m_pDevice->DrawNormalText(nChars, pCharPos,
-                                                     pFont, pCache,
-                                                     font_size, pObject2Device,
-                                                     color, 0 /* text_flags */,
-                                                     alpha_flag, pIccTransform);
+            bTemporaryEnableVectorForTextStroking = true;
+            bool bRet = m_pDevice->DrawNormalText(nChars, pCharPos,
+                                                     pFont,
+                                                     font_size, mtObject2Device,
+                                                     color, 0 /* text_flags */);
             bTemporaryEnableVectorForTextStroking = FALSE;
             return bRet;
         }
         else
-            return TRUE; // pretend that we did the job
-        //return m_poParent->DrawDeviceText(nChars, pCharPos, pFont,
-        //                                  pCache, pObject2Device, font_size, color,
-        //                                  alpha_flag, pIccTransform);
+            return true; // pretend that we did the job
     }
 
-    virtual void*       GetPlatformSurface() override
+    virtual int         GetDriverType() const override
     {
-        return m_poParent->GetPlatformSurface();
+        return m_poParent->GetDriverType();
     }
 
-    virtual int         GetDriverType() override
+    virtual void    ClearDriver() override { m_poParent->ClearDriver(); }
+
+    virtual bool DrawShading(const CPDF_ShadingPattern* pPattern,
+                            const CFX_Matrix* pMatrix,
+                            const FX_RECT& clip_rect,
+                            int alpha,
+                            bool bAlphaMode) override
     {
-        return m_poParent->GetDriverType();
+        if( !bEnableBitmap && !bTemporaryEnableVectorForTextStroking )
+            return true;
+        return m_poParent->DrawShading(pPattern, pMatrix, clip_rect, alpha, bAlphaMode);
     }
 
-    virtual void    ClearDriver() override { m_poParent->ClearDriver(); }
+    virtual bool SetBitsWithMask(const RetainPtr<CFX_DIBBase>& pBitmap,
+                                const RetainPtr<CFX_DIBBase>& pMask,
+                                int left,
+                                int top,
+                                int bitmap_alpha,
+                                BlendMode blend_type) override
+    {
+        if( !bEnableBitmap && !bTemporaryEnableVectorForTextStroking )
+            return true;
+        return m_poParent->SetBitsWithMask(pBitmap, pMask, left, top, bitmap_alpha, blend_type);
+    }
+#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
+    virtual void Flush() override { return m_poParent->Flush(); }
+#endif
 };
 
 /************************************************************************/
 /*                         PDFiumRenderPageBitmap()                     */
 /************************************************************************/
 
-/* This method is a customization of FPDF_RenderPageBitmap() and FPDF_RenderPage_Retail()
-   from pdfium/fpdfsdk/src/fpdfview.cpp to allow selection of which OGC/layer are
+/* This method is a customization of FPDF_RenderPageBitmap()
+   from pdfium/fpdfsdk/fpdf_view.cpp to allow selection of which OGC/layer are
    active. Thus it inherits the following license */
 // Copyright 2014 PDFium Authors. All rights reserved.
 //
@@ -1618,11 +1631,168 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+static
+void myRenderPageImpl(PDFDataset* poDS,
+                      CPDF_PageRenderContext* pContext,
+                    CPDF_Page* pPage,
+                    const CFX_Matrix& matrix,
+                    const FX_RECT& clipping_rect,
+                    int flags,
+                    bool bNeedToRestore,
+                    CPDFSDK_PauseAdapter* pause) {
+  if (!pContext->m_pOptions)
+    pContext->m_pOptions = pdfium::MakeUnique<CPDF_RenderOptions>();
+
+  auto& options = pContext->m_pOptions->GetOptions();
+  options.bClearType = !!(flags & FPDF_LCD_TEXT);
+  options.bNoNativeText = !!(flags & FPDF_NO_NATIVETEXT);
+  options.bLimitedImageCache = !!(flags & FPDF_RENDER_LIMITEDIMAGECACHE);
+  options.bForceHalftone = !!(flags & FPDF_RENDER_FORCEHALFTONE);
+  options.bNoTextSmooth = !!(flags & FPDF_RENDER_NO_SMOOTHTEXT);
+  options.bNoImageSmooth = !!(flags & FPDF_RENDER_NO_SMOOTHIMAGE);
+  options.bNoPathSmooth = !!(flags & FPDF_RENDER_NO_SMOOTHPATH);
+
+  // Grayscale output
+  if (flags & FPDF_GRAYSCALE)
+    pContext->m_pOptions->SetColorMode(CPDF_RenderOptions::kGray);
+
+  const CPDF_OCContext::UsageType usage =
+      (flags & FPDF_PRINTING) ? CPDF_OCContext::Print : CPDF_OCContext::View;
+  pContext->m_pOptions->SetOCContext(
+      pdfium::MakeRetain<GDALPDFiumOCContext>(poDS, pPage->GetDocument(), usage));
+
+  pContext->m_pDevice->SaveState();
+  pContext->m_pDevice->SetClip_Rect(clipping_rect);
+  pContext->m_pContext = pdfium::MakeUnique<CPDF_RenderContext>(pPage);
+  pContext->m_pContext->AppendLayer(pPage, &matrix);
+
+  if (flags & FPDF_ANNOT) {
+    auto pOwnedList = pdfium::MakeUnique<CPDF_AnnotList>(pPage);
+    CPDF_AnnotList* pList = pOwnedList.get();
+    pContext->m_pAnnots = std::move(pOwnedList);
+    bool bPrinting =
+        pContext->m_pDevice->GetDeviceType() != DeviceType::kDisplay;
+    pList->DisplayAnnots(pPage, pContext->m_pContext.get(), bPrinting, &matrix,
+                         false, nullptr);
+  }
+
+  pContext->m_pRenderer = pdfium::MakeUnique<CPDF_ProgressiveRenderer>(
+      pContext->m_pContext.get(), pContext->m_pDevice.get(),
+      pContext->m_pOptions.get());
+  pContext->m_pRenderer->Start(pause);
+  if (bNeedToRestore)
+    pContext->m_pDevice->RestoreState(false);
+}
+
+static
+void myRenderPageWithContext(PDFDataset* poDS, 
+                             CPDF_PageRenderContext* pContext,
+                           FPDF_PAGE page,
+                           int start_x,
+                           int start_y,
+                           int size_x,
+                           int size_y,
+                           int rotate,
+                           int flags,
+                           bool bNeedToRestore,
+                           CPDFSDK_PauseAdapter* pause) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return;
+
+  const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y);
+  myRenderPageImpl(poDS, pContext, pPage, pPage->GetDisplayMatrix(rect, rotate), rect,
+                 flags, bNeedToRestore, pause);
+}
+
+ // Substitution for CFX_DefaultRenderDevice::Attach
+static bool myDeviceAttach(CFX_DefaultRenderDevice* pDevice,
+                         const RetainPtr<CFX_DIBitmap>& pBitmap,
+              bool bRgbByteOrder,
+              const RetainPtr<CFX_DIBitmap>& pBackdropBitmap,
+              bool bGroupKnockout,
+              const char* pszRenderingOptions)
+{
+  pDevice->SetBitmap(pBitmap);
+
+  std::unique_ptr<RenderDeviceDriverIface> driver = pdfium::MakeUnique<CFX_AggDeviceDriver>(
+      pBitmap, bRgbByteOrder, pBackdropBitmap, bGroupKnockout);
+  if (pszRenderingOptions != nullptr)
+  {
+    int bEnableVector = FALSE;
+    int bEnableText = FALSE;
+    int bEnableBitmap = FALSE;
+
+    char** papszTokens = CSLTokenizeString2( pszRenderingOptions, " ,", 0 );
+    for(int i=0;papszTokens[i] != nullptr;i++)
+    {
+        if (EQUAL(papszTokens[i], "VECTOR"))
+            bEnableVector = TRUE;
+        else if (EQUAL(papszTokens[i], "TEXT"))
+            bEnableText = TRUE;
+        else if (EQUAL(papszTokens[i], "RASTER") ||
+                    EQUAL(papszTokens[i], "BITMAP"))
+            bEnableBitmap = TRUE;
+        else
+        {
+            CPLError(CE_Warning, CPLE_NotSupported,
+                        "Value %s is not a valid value for GDAL_PDF_RENDERING_OPTIONS",
+                        papszTokens[i]);
+        }
+    }
+    CSLDestroy(papszTokens);
+
+    if( !bEnableVector || !bEnableText || !bEnableBitmap )
+    {
+        std::unique_ptr<GDALPDFiumRenderDeviceDriver> poGDALRDDriver =
+            pdfium::MakeUnique<GDALPDFiumRenderDeviceDriver>(std::move(driver), pDevice);
+        poGDALRDDriver->SetEnableVector(bEnableVector);
+        poGDALRDDriver->SetEnableText(bEnableText);
+        poGDALRDDriver->SetEnableBitmap(bEnableBitmap);
+        driver = std::move(poGDALRDDriver);
+    }
+  }
+
+  pDevice->SetDeviceDriver(std::move(driver));
+  return true;
+}
+
 void PDFDataset::PDFiumRenderPageBitmap(FPDF_BITMAP bitmap, FPDF_PAGE page,
                                         int start_x, int start_y,
                                         int size_x, int size_y,
                                         const char* pszRenderingOptions)
 {
+  const int rotate = 0;
+  const int flags = 0;
+
+  if (!bitmap)
+    return;
+
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return;
+
+  CPDF_PageRenderContext* pContext = new CPDF_PageRenderContext;
+  pPage->SetRenderContext(pdfium::WrapUnique(pContext));
+
+  CFX_DefaultRenderDevice* pDevice = new CFX_DefaultRenderDevice;
+  pContext->m_pDevice.reset(pDevice);
+
+  RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap));
+
+  // Substitution for pDevice->Attach(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, false);
+  myDeviceAttach(pDevice, pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, false, pszRenderingOptions);
+
+  myRenderPageWithContext(this, pContext, page, start_x, start_y, size_x, size_y,
+                        rotate, flags, true, nullptr);
+
+#ifdef _SKIA_SUPPORT_PATHS_
+  pDevice->Flush(true);
+  pBitmap->UnPreMultiply();
+#endif
+  pPage->SetRenderContext(nullptr);
+
+#if 0
     CPDF_Page* pPage = (CPDF_Page*)page;
 
     CRenderContext* pContext = new CRenderContext;
@@ -1679,7 +1849,7 @@
     pContext->m_pOptions->m_pOCContext = new GDALPDFiumOCContext(
         poParentDS ? poParentDS: this, pPage->m_pDocument);
 
-    CFX_AffineMatrix matrix;
+    CFX_Matrix matrix;
     pPage->GetDisplayMatrix(matrix, start_x, start_y, size_x, size_y, 0);
 
     FX_RECT clip;
@@ -1701,6 +1871,7 @@
 
     delete pContext;
     pPage->RemovePrivateData((void*)1);
+#endif
 }
 
 #endif /* HAVE_PDFIUM */
@@ -1758,11 +1929,7 @@
         }
 
         PDFDoc* poDoc = poDocPoppler;
-#ifdef POPPLER_0_20_OR_LATER
         poSplashOut->startDoc(poDoc);
-#else
-        poSplashOut->startDoc(poDoc->getXRef());
-#endif
 
         /* EVIL: we modify a private member... */
         /* poppler (at least 0.12 and 0.14 versions) don't render correctly */
@@ -1770,12 +1937,10 @@
         /* in those cases. This processing of optional content is an addition of */
         /* poppler in comparison to original xpdf, which hasn't the issue. All in */
         /* all, nullifying optContent removes the error message and improves the rendering */
-#ifdef POPPLER_HAS_OPTCONTENT
         Catalog* poCatalog = poDoc->getCatalog();
         OCGs* poOldOCGs = poCatalog->optContent;
         if (!bUseOCG)
             poCatalog->optContent = nullptr;
-#endif
         poDoc->displayPageSlice(poSplashOut,
                                 iPage,
                                 dfDPI, dfDPI,
@@ -1785,9 +1950,7 @@
                                 nReqXSize, nReqYSize);
 
     /* Restore back */
-#ifdef POPPLER_HAS_OPTCONTENT
         poCatalog->optContent = poOldOCGs;
-#endif
 
         SplashBitmap* poBitmap = poSplashOut->getBitmap();
         if (poBitmap->getWidth() != nReqXSize || poBitmap->getHeight() != nReqYSize)
@@ -2034,7 +2197,7 @@
 
         // Part of PDF is render with -x, -y, page_width, page_height
         // (not requested size!)
-        PDFiumRenderPageBitmap(bitmap, poPagePdfium->page,
+        PDFiumRenderPageBitmap(bitmap, FPDFPageFromIPDFPage(poPagePdfium->page),
               -nReqXOff, -nReqYOff, nRasterXSize, nRasterYSize, pszRenderingOptions);
 
         int stride = FPDFBitmap_GetStride(bitmap);
@@ -2294,7 +2457,7 @@
     if (bUseLib.test(PDFLIB_POPPLER))
     {
         poCatalogObjectPoppler = new ObjectAutoFree;
-#ifdef POPPLER_0_58_OR_LATER
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 58
         *poCatalogObjectPoppler->getObj() = poDocPoppler->getXRef()->getCatalog();
 #else
         poDocPoppler->getXRef()->getCatalog(poCatalogObjectPoppler->getObj());
@@ -2312,10 +2475,10 @@
         VSILFILE* fp = VSIFOpenL(osFilename.c_str(), "rb");
         if (fp != nullptr)
         {
-            GDALPDFWriter oWriter(fp, TRUE);
+            GDALPDFUpdateWriter oWriter(fp);
             if (oWriter.ParseTrailerAndXRef())
             {
-                nCatalogNum = oWriter.GetCatalogNum();
+                nCatalogNum = oWriter.GetCatalogNum().toInt();
                 nCatalogGen = oWriter.GetCatalogGen();
             }
             oWriter.Close();
@@ -2368,7 +2531,7 @@
     GDALPDFDictionaryRW* poCatalogDictCopy = nullptr;
     if( poPageObj )
     {
-        nNum = poPageObj->GetRefNum();
+        nNum = poPageObj->GetRefNum().toInt();
         nGen = poPageObj->GetRefGen();
         if (eAccess == GA_Update &&
             (bProjDirty || bNeatLineDirty || bInfoDirty || bXMPDirty) &&
@@ -2424,12 +2587,12 @@
         VSILFILE* fp = VSIFOpenL(osFilename, "rb+");
         if (fp != nullptr)
         {
-            GDALPDFWriter oWriter(fp, TRUE);
+            GDALPDFUpdateWriter oWriter(fp);
             if (oWriter.ParseTrailerAndXRef())
             {
                 if ((bProjDirty || bNeatLineDirty) && poPageDictCopy != nullptr)
                     oWriter.UpdateProj(this, dfDPI,
-                                        poPageDictCopy, nNum, nGen);
+                                        poPageDictCopy, GDALPDFObjectNum(nNum), nGen);
 
                 if (bInfoDirty)
                     oWriter.UpdateInfo(this);
@@ -2592,34 +2755,31 @@
     CPLError(CE_Failure, CPLE_AppDefined, "%s", osError.c_str());
 }
 
-#ifdef POPPLER_0_20_OR_LATER
-static void PDFDatasetErrorFunction(CPL_UNUSED void* userData, CPL_UNUSED ErrorCategory eErrCategory,
-#ifdef POPPLER_0_23_OR_LATER
+static int g_nPopplerErrors = 0;
+constexpr int MAX_POPPLER_ERRORS = 1000;
+
+static void PDFDatasetErrorFunction(void* /* userData*/,
+                                    ErrorCategory /* eErrCategory */,
                                     Goffset nPos,
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 71
+                                    const char *pszMsg
 #else
-                                    int nPos,
+                                    char *pszMsg
 #endif
-                                    char *pszMsg)
+                                   )
 {
-    CPLString osError;
+    if( g_nPopplerErrors >= MAX_POPPLER_ERRORS )
+        return;
 
-    if (nPos >= 0)
-        osError.Printf("Pos = %d, ", (int)nPos);
-    osError += pszMsg;
-    PDFDatasetErrorFunctionCommon(osError);
-}
-#else
-static void PDFDatasetErrorFunction(int nPos, char *pszMsg, va_list args)
-{
+    g_nPopplerErrors ++;
     CPLString osError;
 
     if (nPos >= 0)
-        osError.Printf("Pos = %d, ", nPos);
-    osError += CPLString().vPrintf(pszMsg, args);
+        osError.Printf("Pos = " CPL_FRMT_GUIB ", ", static_cast<GUIntBig>(nPos));
+    osError += pszMsg;
     PDFDatasetErrorFunctionCommon(osError);
 }
 #endif
-#endif
 
 /************************************************************************/
 /*                GDALPDFParseStreamContentOnlyDrawForm()               */
@@ -2818,21 +2978,27 @@
                                 double dfHeight = Get(poHeight);
                                 double dfScaleX = adfVals[0];
                                 double dfScaleY = adfVals[3];
-                                double dfDPI_X = ROUND_TO_INT_IF_CLOSE(dfWidth / dfScaleX * DEFAULT_DPI, 1e-3);
-                                double dfDPI_Y = ROUND_TO_INT_IF_CLOSE(dfHeight / dfScaleY * DEFAULT_DPI, 1e-3);
-                                //CPLDebug("PDF", "Image %s, width = %.16g, height = %.16g, scaleX = %.16g, scaleY = %.16g --> DPI_X = %.16g, DPI_Y = %.16g",
-                                //                osCurrentImage.c_str(), dfWidth, dfHeight, dfScaleX, dfScaleY, dfDPI_X, dfDPI_Y);
-                                if (dfDPI_X > dfDPI) dfDPI = dfDPI_X;
-                                if (dfDPI_Y > dfDPI) dfDPI = dfDPI_Y;
-
-                                memcpy(&(sTile.adfCM), adfVals, 6 * sizeof(double));
-                                sTile.poImage = poImage;
-                                sTile.dfWidth = dfWidth;
-                                sTile.dfHeight = dfHeight;
-                                asTiles.push_back(sTile);
+                                if( dfWidth > 0 && dfHeight > 0 &&
+                                    dfScaleX > 0 && dfScaleY > 0 &&
+                                    dfWidth / dfScaleX * DEFAULT_DPI < INT_MAX &&
+                                    dfHeight / dfScaleY * DEFAULT_DPI < INT_MAX )
+                                {
+                                    double dfDPI_X = ROUND_TO_INT_IF_CLOSE(dfWidth / dfScaleX * DEFAULT_DPI, 1e-3);
+                                    double dfDPI_Y = ROUND_TO_INT_IF_CLOSE(dfHeight / dfScaleY * DEFAULT_DPI, 1e-3);
+                                    //CPLDebug("PDF", "Image %s, width = %.16g, height = %.16g, scaleX = %.16g, scaleY = %.16g --> DPI_X = %.16g, DPI_Y = %.16g",
+                                    //                osCurrentImage.c_str(), dfWidth, dfHeight, dfScaleX, dfScaleY, dfDPI_X, dfDPI_Y);
+                                    if (dfDPI_X > dfDPI) dfDPI = dfDPI_X;
+                                    if (dfDPI_Y > dfDPI) dfDPI = dfDPI_Y;
+
+                                    memcpy(&(sTile.adfCM), adfVals, 6 * sizeof(double));
+                                    sTile.poImage = poImage;
+                                    sTile.dfWidth = dfWidth;
+                                    sTile.dfHeight = dfHeight;
+                                    asTiles.push_back(sTile);
 
-                                *pbDPISet = TRUE;
-                                *pdfDPI = dfDPI;
+                                    *pbDPISet = TRUE;
+                                    *pdfDPI = dfDPI;
+                                }
                             }
                         }
                         nState = STATE_INIT;
@@ -2990,6 +3156,7 @@
     const char* pszDPI = GetOption(papszOpenOptions, "DPI", nullptr);
     if (pszDPI != nullptr)
     {
+        // coverity[tainted_data]
         dfDPI = CPLAtof(pszDPI);
     }
     else
@@ -3375,6 +3542,8 @@
     for(int i=0;i<nLength;i++)
     {
         GDALPDFObject* poObj = poArray->Get(i);
+        if( poObj == nullptr )
+            continue;
         if (i == 0 && poObj->GetType() == PDFObjectType_String)
         {
             CPLString osName = PDFSanitizeLayerName(poObj->GetString().c_str());
@@ -3383,7 +3552,8 @@
             else
                 osTopLayer = osName;
             AddLayer(osTopLayer.c_str());
-            oLayerOCGMapPoppler[osTopLayer.c_str()] = nullptr;
+            oLayerOCGListPoppler.push_back(
+                std::pair<CPLString, OptionalContentGroup*>(osTopLayer, nullptr));
         }
         else if (poObj->GetType() == PDFObjectType_Array)
         {
@@ -3406,15 +3576,15 @@
 
                 OCGs* optContentConfig = poDocPoppler->getOptContentConfig();
                 struct Ref r;
-                r.num = poObj->GetRefNum();
+                r.num = poObj->GetRefNum().toInt();
                 r.gen = poObj->GetRefGen();
                 OptionalContentGroup* ocg = optContentConfig->findOcgByRef(r);
                 if (ocg)
                 {
                     AddLayer(osCurLayer.c_str());
-                    oLayerOCGMapPoppler[osCurLayer.c_str()] = ocg;
-                    osLayerWithRefList.AddString(
-                        CPLSPrintf("%s %d %d", osCurLayer.c_str(), r.num, r.gen));
+                    oLayerOCGListPoppler.push_back(std::make_pair(osCurLayer, ocg));
+                    aoLayerWithRef.emplace_back(
+                        osCurLayer.c_str(), poObj->GetRefNum(), r.gen);
                 }
             }
         }
@@ -3440,15 +3610,25 @@
     }
     else
     {
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 69
+        for( const auto& refOCGPair: optContentConfig->getOCGs() )
+        {
+            auto ocg = refOCGPair.second.get();
+#else
         GooList* ocgList = optContentConfig->getOCGs();
         for(int i=0;i<ocgList->getLength();i++)
         {
             OptionalContentGroup* ocg = (OptionalContentGroup*) ocgList->get(i);
+#endif
             if( ocg != nullptr && ocg->getName() != nullptr )
             {
+#if (POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 72)
+                const char* pszLayerName = (const char*)ocg->getName()->c_str();
+#else
                 const char* pszLayerName = (const char*)ocg->getName()->getCString();
+#endif
                 AddLayer(pszLayerName);
-                oLayerOCGMapPoppler[pszLayerName] = ocg;
+                oLayerOCGListPoppler.push_back(std::make_pair(CPLString(pszLayerName), ocg));
             }
         }
     }
@@ -3472,20 +3652,31 @@
     {
         int i;
         int bAll = EQUAL(pszLayers, "ALL");
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 69
+        for( const auto& refOCGPair: optContentConfig->getOCGs() )
+        {
+            auto ocg = refOCGPair.second.get();
+#else
         GooList* ocgList = optContentConfig->getOCGs();
         for(i=0;i<ocgList->getLength();i++)
         {
             OptionalContentGroup* ocg = (OptionalContentGroup*) ocgList->get(i);
+#endif
             ocg->setState( (bAll) ? OptionalContentGroup::On : OptionalContentGroup::Off );
         }
 
         char** papszLayers = CSLTokenizeString2(pszLayers, ",", 0);
         for(i=0;!bAll && papszLayers[i] != nullptr;i++)
         {
-            std::map<CPLString, OptionalContentGroup*>::iterator oIter =
-                oLayerOCGMapPoppler.find(papszLayers[i]);
-            if (oIter != oLayerOCGMapPoppler.end())
+            bool isFound = false;
+            for (auto oIter2 = oLayerOCGListPoppler.begin();
+                oIter2 != oLayerOCGListPoppler.end(); ++oIter2)
             {
+                if (oIter2->first != papszLayers[i])
+                    continue;
+
+                isFound = true;
+                auto oIter = oIter2;
                 if (oIter->second)
                 {
                     //CPLDebug("PDF", "Turn '%s' on", papszLayers[i]);
@@ -3496,8 +3687,8 @@
                 // in the list.
                 size_t nLen = strlen(papszLayers[i]);
                 int bFoundChildLayer = FALSE;
-                oIter = oLayerOCGMapPoppler.begin();
-                for( ; oIter != oLayerOCGMapPoppler.end() && !bFoundChildLayer; oIter ++)
+                oIter = oLayerOCGListPoppler.begin();
+                for (; oIter != oLayerOCGListPoppler.end() && !bFoundChildLayer; ++oIter)
                 {
                     if (oIter->first.size() > nLen &&
                         strncmp(oIter->first.c_str(), papszLayers[i], nLen) == 0 &&
@@ -3506,15 +3697,18 @@
                         for(int j=0;papszLayers[j] != nullptr;j++)
                         {
                             if (strcmp(papszLayers[j], oIter->first.c_str()) == 0)
+                            {
                                 bFoundChildLayer = TRUE;
+                                break;
+                            }
                         }
                     }
                 }
 
                 if( !bFoundChildLayer )
                 {
-                    oIter = oLayerOCGMapPoppler.begin();
-                    for( ; oIter != oLayerOCGMapPoppler.end() && !bFoundChildLayer; oIter ++)
+                    oIter = oLayerOCGListPoppler.begin();
+                    for (; oIter != oLayerOCGListPoppler.end() && !bFoundChildLayer; ++oIter)
                     {
                         if (oIter->first.size() > nLen &&
                             strncmp(oIter->first.c_str(), papszLayers[i], nLen) == 0 &&
@@ -3530,22 +3724,23 @@
                 }
 
                 // Turn parent layers on too
-                char* pszLastDot = nullptr;
-                while( (pszLastDot = strrchr(papszLayers[i], '.')) != nullptr)
-                {
-                    *pszLastDot = '\0';
-                    oIter = oLayerOCGMapPoppler.find(papszLayers[i]);
-                    if (oIter != oLayerOCGMapPoppler.end())
+                std::string layer(papszLayers[i]);
+                std::string::size_type j;
+                while ((j = layer.find_last_of('.')) != std::string::npos)
+                {
+                    layer.resize(j);
+                    oIter = oLayerOCGListPoppler.begin();
+                    for (; oIter != oLayerOCGListPoppler.end(); ++oIter)
                     {
-                        if (oIter->second)
+                        if (oIter->first == layer && oIter->second)
                         {
-                            //CPLDebug("PDF", "Turn '%s' on too", papszLayers[i]);
+                            //CPLDebug("PDF", "Turn '%s' on too", layer.c_str());
                             oIter->second->setState(OptionalContentGroup::On);
                         }
                     }
                 }
             }
-            else
+            if (!isFound)
             {
                 CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'",
                             papszLayers[i]);
@@ -3563,10 +3758,15 @@
         char** papszLayersOFF = CSLTokenizeString2(pszLayersOFF, ",", 0);
         for(int i=0;papszLayersOFF[i] != nullptr;i++)
         {
-            std::map<CPLString, OptionalContentGroup*>::iterator oIter =
-                oLayerOCGMapPoppler.find(papszLayersOFF[i]);
-            if (oIter != oLayerOCGMapPoppler.end())
+            bool isFound = false;
+            for (auto oIter2 = oLayerOCGListPoppler.begin();
+                oIter2 != oLayerOCGListPoppler.end(); ++oIter2)
             {
+                if (oIter2->first != papszLayersOFF[i])
+                    continue;
+
+                isFound = true;
+                auto oIter = oIter2;
                 if (oIter->second)
                 {
                     //CPLDebug("PDF", "Turn '%s' off", papszLayersOFF[i]);
@@ -3575,8 +3775,8 @@
 
                 // Turn child layers off too
                 size_t nLen = strlen(papszLayersOFF[i]);
-                oIter = oLayerOCGMapPoppler.begin();
-                for( ; oIter != oLayerOCGMapPoppler.end(); oIter ++)
+                oIter = oLayerOCGListPoppler.begin();
+                for (; oIter != oLayerOCGListPoppler.end(); ++oIter)
                 {
                     if (oIter->first.size() > nLen &&
                         strncmp(oIter->first.c_str(), papszLayersOFF[i], nLen) == 0 &&
@@ -3590,7 +3790,7 @@
                     }
                 }
             }
-            else
+            if (!isFound)
             {
                 CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'",
                             papszLayersOFF[i]);
@@ -3652,10 +3852,9 @@
                 //CPLDebug("PDF", "Layer %s", osCurLayer.c_str());
 
                 AddLayer(osCurLayer.c_str());
-                osLayerWithRefList.AddString(
-                    CPLSPrintf("%s %d %d", osCurLayer.c_str(), poObj->GetRefNum(), poObj->GetRefGen()));
+                aoLayerWithRef.emplace_back(osCurLayer, poObj->GetRefNum(), poObj->GetRefGen());
                 oMapLayerNameToOCGNumGenPdfium[osCurLayer] =
-                    std::pair<int,int>(poObj->GetRefNum(), poObj->GetRefGen());
+                    std::pair<int,int>(poObj->GetRefNum().toInt(), poObj->GetRefGen());
             }
         }
     }
@@ -3723,7 +3922,7 @@
         for(i=0;i<nLength;i++)
         {
             GDALPDFObject* poOCG = poOCGsArray->Get(i);
-            oMapOCGNumGenToVisibilityStatePdfium[ std::pair<int,int>(poOCG->GetRefNum(), poOCG->GetRefGen()) ] =
+            oMapOCGNumGenToVisibilityStatePdfium[ std::pair<int,int>(poOCG->GetRefNum().toInt(), poOCG->GetRefGen()) ] =
                 (bAll) ? VISIBILITY_ON : VISIBILITY_OFF;
         }
 
@@ -3885,7 +4084,7 @@
         for(; oIter != oEnd; ++oIter)
         {
             GDALPDFObject* poObj = oIter->second;
-            if( poObj->GetRefNum() != 0 && poObj->GetType() == PDFObjectType_Dictionary )
+            if( poObj->GetRefNum().toBool() && poObj->GetType() == PDFObjectType_Dictionary )
             {
                 GDALPDFObject* poType = poObj->GetDictionary()->Get("Type");
                 GDALPDFObject* poName = poObj->GetDictionary()->Get("Name");
@@ -3923,7 +4122,7 @@
         for(; oIter != oEnd; ++oIter)
         {
             GDALPDFObject* poObj = oIter->second;
-            if( poObj->GetRefNum() != 0 && poObj->GetType() == PDFObjectType_Dictionary )
+            if( poObj->GetRefNum().toBool() && poObj->GetType() == PDFObjectType_Dictionary )
             {
                 GDALPDFObject* poType = poObj->GetDictionary()->Get("Type");
                 GDALPDFObject* poName = poObj->GetDictionary()->Get("Name");
@@ -3933,11 +4132,10 @@
                     poName != nullptr &&
                     poName->GetType() == PDFObjectType_String )
                 {
-                    osLayerWithRefList.AddString(
-                        CPLSPrintf("%s %d %d",
-                                    PDFSanitizeLayerName(poName->GetString()).c_str(),
-                                    poObj->GetRefNum(),
-                                    poObj->GetRefGen()));
+                    aoLayerWithRef.emplace_back(
+                        PDFSanitizeLayerName(poName->GetString()).c_str(),
+                        poObj->GetRefNum(),
+                        poObj->GetRefGen());
                 }
             }
         }
@@ -3948,7 +4146,7 @@
 /*                                Open()                                */
 /************************************************************************/
 
-GDALDataset *PDFDataset::Open( GDALOpenInfo * poOpenInfo )
+PDFDataset *PDFDataset::Open( GDALOpenInfo * poOpenInfo )
 
 {
     if (!Identify(poOpenInfo))
@@ -4039,7 +4237,7 @@
     GDALPDFObject* poPageObj = nullptr;
 #ifdef HAVE_POPPLER
     PDFDoc* poDocPoppler = nullptr;
-#ifdef POPPLER_0_58_OR_LATER
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 58
     Object oObj;
 #else
     ObjectAutoFree oObj;
@@ -4063,17 +4261,19 @@
     GooString* poUserPwd = nullptr;
 
     /* Set custom error handler for poppler errors */
-#ifdef POPPLER_0_20_OR_LATER
     setErrorCallback(PDFDatasetErrorFunction, nullptr);
-#else
-    setErrorFunction(PDFDatasetErrorFunction);
-#endif
 
     {
         CPLMutexHolderD(&hGlobalParamsMutex);
         /* poppler global variable */
         if (globalParams == nullptr)
+        {
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 83
+            globalParams.reset(new GlobalParams());
+#else
             globalParams = new GlobalParams();
+#endif
+        }
 
         globalParams->setPrintCommands(CPLTestBool(
             CPLGetConfigOption("GDAL_PDF_PRINT_COMMANDS", "FALSE")));
@@ -4090,13 +4290,20 @@
         if (pszUserPwd)
             poUserPwd = new GooString(pszUserPwd);
 
-#ifdef POPPLER_0_58_OR_LATER
-        poDocPoppler = new PDFDoc(new VSIPDFFileStream(fp, pszFilename, std::move(oObj)), nullptr, poUserPwd);
+        g_nPopplerErrors = 0;
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 58
+        auto poStream = new VSIPDFFileStream(fp, pszFilename, std::move(oObj));
 #else
         oObj.getObj()->initNull();
-        poDocPoppler = new PDFDoc(new VSIPDFFileStream(fp, pszFilename, oObj.getObj()), nullptr, poUserPwd);
+        auto poStream = new VSIPDFFileStream(fp, pszFilename, oObj.getObj());
 #endif
+        poDocPoppler = new PDFDoc(poStream, nullptr, poUserPwd);
         delete poUserPwd;
+        if( g_nPopplerErrors >= MAX_POPPLER_ERRORS )
+        {
+            PDFFreeDoc(poDocPoppler);
+            return nullptr;
+        }
 
         if ( !poDocPoppler->isOk() || poDocPoppler->getNumPages() == 0 )
         {
@@ -4133,8 +4340,25 @@
 
             return nullptr;
         }
+        else if( poDocPoppler->isLinearized() &&
+                 !poStream->FoundLinearizedHint() )
+        {
+            // This is a likely defect of poppler Linearization.cc file that
+            // recognizes a file as linearized if the /Linearized hint is missing,
+            // but the content of this dictionary are present.
+            // But given the hacks of PDFFreeDoc() and VSIPDFFileStream::FillBuffer()
+            // opening such a file will result in a null-ptr deref at closing if
+            // we try to access a page and build the page cache, so just exit now
+            CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF");
+
+            PDFFreeDoc(poDocPoppler);
+
+            return nullptr;
+        }
         else
+        {
             break;
+        }
     }
 
     poCatalogPoppler = poDocPoppler->getCatalog();
@@ -4200,7 +4424,7 @@
     Ref* poPageRef = poCatalogPoppler->getPageRef(iPage);
     if (poPageRef != nullptr)
     {
-        ((GDALPDFObjectPoppler*)poPageObj)->SetRefNumAndGen(poPageRef->num, poPageRef->gen);
+        ((GDALPDFObjectPoppler*)poPageObj)->SetRefNumAndGen(GDALPDFObjectNum(poPageRef->num), poPageRef->gen);
     }
   }
 #endif  // ~ HAVE_POPPLER
@@ -4322,7 +4546,7 @@
         return nullptr;
     }
 
-    CPDF_Object* pageObj = poPagePdfium->page->m_pFormDict;
+    CPDF_Object* pageObj = poPagePdfium->page->GetDict();
     if(pageObj == nullptr) {
         CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid page object");
         UnloadPdfiumDocumentPage(&poDocPdfium, &poPagePdfium);
@@ -4418,6 +4642,7 @@
         const char* pszDPI = GetOption(poOpenInfo->papszOpenOptions, "DPI", nullptr);
         if (pszDPI != nullptr)
         {
+            // coverity[tainted_data]
             poDS->dfDPI = CPLAtof(pszDPI);
         }
     }
@@ -4430,7 +4655,7 @@
 #ifdef HAVE_POPPLER
     if (bUseLib.test(PDFLIB_POPPLER))
     {
-        PDFRectangle* psMediaBox = poPagePoppler->getMediaBox();
+        const auto* psMediaBox = poPagePoppler->getMediaBox();
         dfX1 = psMediaBox->x1;
         dfY1 = psMediaBox->y1;
         dfX2 = psMediaBox->x2;
@@ -4452,7 +4677,7 @@
 
 #ifdef HAVE_PDFIUM
     if (bUseLib.test(PDFLIB_PDFIUM)) {
-        CFX_FloatRect rect = poPagePdfium->page->GetPageBBox();
+        CFX_FloatRect rect = poPagePdfium->page->GetBBox();
         dfX1 = rect.left;
         dfX2 = rect.right;
         dfY1 = rect.bottom;
@@ -4490,11 +4715,7 @@
 #ifdef HAVE_PDFIUM
     if (bUseLib.test(PDFLIB_PDFIUM))
     {
-        CPDF_Object* pRotate = poPagePdfium->page->GetPageAttr(FX_BSTRC("Rotate"));
-        if (pRotate)
-          dfRotation = pRotate->GetInteger();
-        if(dfRotation < 0)
-          dfRotation += 360.0;
+        dfRotation = poPagePdfium->page->GetPageRotation() * 90;
     }
 #endif
 
@@ -4640,11 +4861,11 @@
                     {
                         if (nImageNum < 0)
                             CPLDebug("PDF", "Measure found on Image object (%d)",
-                                     poObj->GetRefNum());
+                                     poObj->GetRefNum().toInt());
 
                         GDALPDFObject* poColorSpace = poDict->Get("ColorSpace");
                         GDALPDFObject* poBitsPerComponent = poDict->Get("BitsPerComponent");
-                        if (poObj->GetRefNum() != 0 &&
+                        if (poObj->GetRefNum().toBool() &&
                             poObj->GetRefGen() == 0 &&
                             poColorSpace != nullptr &&
                             poColorSpace->GetType() == PDFObjectType_Name &&
@@ -4660,7 +4881,7 @@
                                 poDS->SetMetadataItem(CPLSPrintf("SUBDATASET_%d_NAME",
                                                                  nSubDataset),
                                                       CPLSPrintf("PDF_IMAGE:%d:%d:%s",
-                                                                 iPage, poObj->GetRefNum(), pszFilename),
+                                                                 iPage, poObj->GetRefNum().toInt(), pszFilename),
                                                       "SUBDATASETS");
                                 poDS->SetMetadataItem(CPLSPrintf("SUBDATASET_%d_DESC",
                                                                  nSubDataset),
@@ -4668,7 +4889,7 @@
                                                                  nW, nH, iPage, pszFilename),
                                                       "SUBDATASETS");
                             }
-                            else if (poObj->GetRefNum() == nImageNum)
+                            else if (poObj->GetRefNum().toInt() == nImageNum)
                             {
                                 poDS->nRasterXSize = nW;
                                 poDS->nRasterYSize = nH;
@@ -4768,14 +4989,16 @@
     GooString* poMetadata = poCatalogPoppler->readMetadata();
     if (poMetadata)
     {
-        char* pszContent = poMetadata->getCString();
+#if (POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 72)
+        const char* pszContent = poMetadata->c_str();
+#else
+        const char* pszContent = poMetadata->getCString();
+#endif
         if (pszContent != nullptr &&
             STARTS_WITH(pszContent, "<?xpacket begin="))
         {
-            char *apszMDList[2];
-            apszMDList[0] = pszContent;
-            apszMDList[1] = nullptr;
-            poDS->SetMetadata(apszMDList, "xml:XMP");
+            const char * const apszMDList[2] = { pszContent, nullptr };
+            poDS->SetMetadata(const_cast<char**>(apszMDList), "xml:XMP");
         }
         delete poMetadata;
     }
@@ -4786,14 +5009,14 @@
     if( poDocPoppler->getXRef()->isOk() )
     {
         Object oInfo;
-#ifdef POPPLER_0_58_OR_LATER
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 58
         oInfo = poDocPoppler->getDocInfo();
 #else
         poDocPoppler->getDocInfo(&oInfo);
 #endif
         GDALPDFObjectPoppler oInfoObjPoppler(&oInfo, FALSE);
         poDS->ParseInfo(&oInfoObjPoppler);
-#ifndef POPPLER_0_58_OR_LATER
+#if !(POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 58)
         oInfo.free();
 #endif
     }
@@ -5022,6 +5245,8 @@
     {
         const char* pszStr = poObj->GetString().c_str();
         size_t nLen = strlen(pszStr);
+        if( nLen == 0 )
+            return 0;
         /* cf Military_Installations_2008.pdf that has values like "96 0 0.0W" */
         char chLast = pszStr[nLen-1];
         if (chLast == 'W' || chLast == 'E' || chLast == 'N' || chLast == 'S')
@@ -5318,6 +5543,12 @@
                     nGCPCount ++;
                 }
             }
+
+            if( nGCPCount == 0 )
+            {
+                CPLFree(pasGCPList);
+                pasGCPList = nullptr;
+            }
         }
     }
 
@@ -6132,7 +6363,6 @@
                              double dfMediaBoxWidth, double dfMediaBoxHeight,
                              double dfULX, double dfULY, double dfLRX, double dfLRY)
 {
-    int i;
     GDALPDFDictionary* poMeasureDict = poMeasure->GetDictionary();
 
 /* -------------------------------------------------------------------- */
@@ -6177,7 +6407,7 @@
         if (nBoundsLength == 8)
         {
             double adfBounds[8];
-            for(i=0;i<8;i++)
+            for(int i=0;i<8;i++)
             {
                 adfBounds[i] = Get(poBounds, i);
                 CPLDebug("PDF", "Bounds[%d] = %f", i, adfBounds[i]);
@@ -6207,15 +6437,15 @@
     }
 
     int nGPTSLength = poGPTS->GetArray()->GetLength();
-    if (nGPTSLength != 8)
+    if ((nGPTSLength % 2) != 0 || nGPTSLength < 6)
     {
         CPLError(CE_Failure, CPLE_AppDefined,
                  "Invalid length for GPTS object");
         return FALSE;
     }
 
-    double adfGPTS[8];
-    for(i=0;i<8;i++)
+    std::vector<double> adfGPTS(nGPTSLength);
+    for(int i=0;i<nGPTSLength;i++)
     {
         adfGPTS[i] = Get(poGPTS, i);
         CPLDebug("PDF", "GPTS[%d] = %.18f", i, adfGPTS[i]);
@@ -6238,15 +6468,15 @@
     }
 
     int nLPTSLength = poLPTS->GetArray()->GetLength();
-    if (nLPTSLength != 8)
+    if (nLPTSLength != nGPTSLength)
     {
         CPLError(CE_Failure, CPLE_AppDefined,
                  "Invalid length for LPTS object");
         return FALSE;
     }
 
-    double adfLPTS[8];
-    for(i=0;i<8;i++)
+    std::vector<double> adfLPTS(nLPTSLength);
+    for(int i=0;i<nLPTSLength;i++)
     {
         adfLPTS[i] = Get(poLPTS, i);
         CPLDebug("PDF", "LPTS[%d] = %f", i, adfLPTS[i]);
@@ -6310,6 +6540,7 @@
     }
 
     OGRSpatialReference oSRS;
+    //oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); // Since GDAL 3.0
     int bSRSOK = FALSE;
     if (nEPSGCode != 0 &&
         oSRS.importFromEPSG(nEPSGCode) == OGRERR_NONE)
@@ -6372,13 +6603,18 @@
     /* ISO 32000 supplement spec, but in (northing, easting). Adobe reader is able to understand that, */
     /* so let's also try to do it with a heuristics. */
 
-    int bReproject = TRUE;
-    if (oSRS.IsProjected() &&
-        (fabs(adfGPTS[0]) > 91 || fabs(adfGPTS[2]) > 91 || fabs(adfGPTS[4]) > 91 || fabs(adfGPTS[6]) > 91 ||
-         fabs(adfGPTS[1]) > 361 || fabs(adfGPTS[3]) > 361 || fabs(adfGPTS[5]) > 361 || fabs(adfGPTS[7]) > 361))
+    bool bReproject = true;
+    if (oSRS.IsProjected() )
     {
-        CPLDebug("PDF", "GPTS coordinates seems to be in (northing, easting), which is non-standard");
-        bReproject = FALSE;
+        for( int i = 0; i < nGPTSLength / 2; i++ )
+        {
+            if( fabs(adfGPTS[2 * i]) > 91 || fabs(adfGPTS[2 * i + 1]) > 361 )
+            {
+                CPLDebug("PDF", "GPTS coordinates seems to be in (northing, easting), which is non-standard");
+                bReproject = false;
+                break;
+            }
+        }
     }
 
     OGRCoordinateTransformation* poCT = nullptr;
@@ -6394,14 +6630,18 @@
         }
     }
 
-    GDAL_GCP asGCPS[4];
+    std::vector<GDAL_GCP> asGCPS(nGPTSLength/ 2);
 
     /* Create NEATLINE */
-    poNeatLine = new OGRPolygon();
-    OGRLinearRing* poRing = new OGRLinearRing();
-    poNeatLine->addRingDirectly(poRing);
+    OGRLinearRing* poRing = nullptr;
+    if( nGPTSLength == 8 )
+    {
+        poNeatLine = new OGRPolygon();
+        poRing = new OGRLinearRing();
+        poNeatLine->addRingDirectly(poRing);
+    }
 
-    for(i=0;i<4;i++)
+    for(int i=0;i<nGPTSLength/ 2;i++)
     {
         /* We probably assume LPTS is 0 or 1 */
         asGCPS[i].dfGCPPixel = (dfULX * (1 - adfLPTS[2*i+0]) + dfLRX * adfLPTS[2*i+0]) / dfMediaBoxWidth * nRasterXSize;
@@ -6431,17 +6671,18 @@
         asGCPS[i].dfGCPX     = x;
         asGCPS[i].dfGCPY     = y;
 
-        poRing->addPoint(x, y);
+        if( poRing )
+            poRing->addPoint(x, y);
     }
 
     delete poSRSGeog;
     delete poCT;
 
-    if (!GDALGCPsToGeoTransform( 4, asGCPS,
+    if (!GDALGCPsToGeoTransform( nGPTSLength/ 2, asGCPS.data(),
                                adfGeoTransform, FALSE))
     {
         CPLDebug("PDF", "Could not compute GT with exact match. Try with approximate");
-        if (!GDALGCPsToGeoTransform( 4, asGCPS,
+        if (!GDALGCPsToGeoTransform( nGPTSLength/ 2, asGCPS.data(),
                                adfGeoTransform, TRUE))
         {
             CPLError(CE_Failure, CPLE_AppDefined,
@@ -6807,14 +7048,14 @@
     return CE_None;
 }
 
-#endif // #if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) || defined(HAVE_PDFIUM)
+#endif // #ifdef HAVE_PDF_READ_SUPPORT
 
 /************************************************************************/
 /*                          GDALPDFOpen()                               */
 /************************************************************************/
 
 GDALDataset* GDALPDFOpen(
-#if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) || defined(HAVE_PDFIUM)
+#ifdef HAVE_PDF_READ_SUPPORT
                          const char* pszFilename,
                          GDALAccess eAccess
 #else
@@ -6823,7 +7064,7 @@
 #endif
                          )
 {
-#if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) || defined(HAVE_PDFIUM)
+#ifdef HAVE_PDF_READ_SUPPORT
     GDALOpenInfo oOpenInfo(pszFilename, eAccess);
     return PDFDataset::Open(&oOpenInfo);
 #else
@@ -6856,12 +7097,12 @@
             CPLCreateOrAcquireMutex(&(pPage->readMutex), PDFIUM_MUTEX_TIMEOUT);
             CPLReleaseMutex(pPage->readMutex);
             CPLDestroyMutex(pPage->readMutex);
-            FPDF_ClosePage(pPage->page);
+            FPDF_ClosePage(FPDFPageFromIPDFPage(pPage->page));
             delete pPage;
             CPLReleaseMutex(g_oPdfiumReadMutex);
           } // ~ foreach page
 
-          FPDF_CloseDocument(pDoc->doc);
+          FPDF_CloseDocument(FPDFDocumentFromCPDFDocument(pDoc->doc));
           CPLFree(pDoc->filename);
           VSIFCloseL((VSILFILE*)pDoc->psFileAccess->m_Param);
           delete pDoc->psFileAccess;
@@ -6888,6 +7129,9 @@
 
 CPLString PDFSanitizeLayerName(const char* pszName)
 {
+    if( !CPLTestBool(CPLGetConfigOption("GDAL_PDF_LAUNDER_LAYER_NAMES", "YES")) )
+        return pszName;
+
     CPLString osName;
     for(int i=0; pszName[i] != '\0'; i++)
     {
@@ -6921,6 +7165,8 @@
     poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_pdf.html" );
     poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "pdf" );
     poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Byte" );
+    poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES,
+                               "Integer Integer64 Real String Date DateTime Time" );
 
 #if defined(HAVE_POPPLER) || defined(HAVE_PDFIUM)
     poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
@@ -6998,14 +7244,15 @@
 "   <Option name='EXCLUSIVE_LAYERS' type='string' description='Comma separated list of layer names, such that only one of those layers can be ON at a time.'/>\n"
 "   <Option name='JAVASCRIPT' type='string' description='Javascript script to embed and run at file opening'/>\n"
 "   <Option name='JAVASCRIPT_FILE' type='string' description='Filename of the Javascript script to embed and run at file opening'/>\n"
+"   <Option name='COMPOSITION_FILE' type='string' description='XML file describing how the PDF should be composed'/>\n"
 "</CreationOptionList>\n" );
 
-#if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) || defined(HAVE_PDFIUM)
+#ifdef HAVE_PDF_READ_SUPPORT
     poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, szOpenOptionList );
-    poDriver->pfnOpen = PDFDataset::Open;
+    poDriver->pfnOpen = PDFDataset::OpenWrapper;
     poDriver->pfnIdentify = PDFDataset::Identify;
     poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" );
-#endif // HAVE_POPPLER || HAVE_PODOFO || defined(HAVE_PDFIUM)
+#endif // HAVE_PDF_READ_SUPPORT
 
     poDriver->pfnCreateCopy = GDALPDFCreateCopy;
     poDriver->pfnCreate = PDFWritableVectorDataset::Create;
diff -urN gdal-2.3.2/frmts/pdf/pdfio.cpp gdal-2.3.2-pdf/frmts/pdf/pdfio.cpp
--- gdal-2.3.2/frmts/pdf/pdfio.cpp	2018-09-21 18:04:33.000000000 +0900
+++ gdal-2.3.2-pdf/frmts/pdf/pdfio.cpp	2019-12-12 12:44:04.922943931 +0900
@@ -2,10 +2,10 @@
  *
  * Project:  PDF driver
  * Purpose:  GDALDataset driver for PDF dataset.
- * Author:   Even Rouault, <even dot rouault at mines dash paris dot org>
+ * Author:   Even Rouault, <even dot rouault at spatialys.com>
  *
  ******************************************************************************
- * Copyright (c) 2010-2013, Even Rouault <even dot rouault at mines-paris dot org>
+ * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -34,9 +34,8 @@
 
 #include "cpl_vsi.h"
 
-CPL_CVSID("$Id: pdfio.cpp 7e07230bbff24eb333608de4dbd460b7312839d0 2017-12-11 19:08:47Z Even Rouault $")
+CPL_CVSID("$Id$")
 
-#ifdef POPPLER_BASE_STREAM_HAS_TWO_ARGS
 /* Poppler 0.31.0 is the first one that needs to know the file size */
 static vsi_l_offset VSIPDFFileStreamGetSize(VSILFILE* f)
 {
@@ -45,7 +44,6 @@
     VSIFSeekL(f, 0, SEEK_SET);
     return nSize;
 }
-#endif
 
 /************************************************************************/
 /*                         VSIPDFFileStream()                           */
@@ -55,12 +53,10 @@
     VSILFILE* fIn, const char* pszFilename,
     makeSubStream_object_type dictA
 ) :
-#ifdef POPPLER_0_58_OR_LATER
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 58
     BaseStream(std::move(dictA), (Goffset)VSIPDFFileStreamGetSize(fIn)),
-#elif defined(POPPLER_BASE_STREAM_HAS_TWO_ARGS)
-    BaseStream(dictA, (setPos_offset_type)VSIPDFFileStreamGetSize(fIn)),
 #else
-    BaseStream(dictA),
+    BaseStream(dictA, (setPos_offset_type)VSIPDFFileStreamGetSize(fIn)),
 #endif
     poParent(nullptr),
     poFilename(new GooString(pszFilename)),
@@ -83,12 +79,10 @@
                                     vsi_l_offset startA, GBool limitedA,
                                     vsi_l_offset lengthA,
                                     makeSubStream_object_type dictA) :
-#ifdef POPPLER_0_58_OR_LATER
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 58
     BaseStream(std::move(dictA), (Goffset)lengthA),
-#elif defined(POPPLER_BASE_STREAM_HAS_TWO_ARGS)
-    BaseStream(dictA, (makeSubStream_offset_type)lengthA),
 #else
-    BaseStream(dictA),
+    BaseStream(dictA, (makeSubStream_offset_type)lengthA),
 #endif
     poParent(poParentIn),
     poFilename(poParentIn->poFilename),
@@ -122,13 +116,13 @@
 /*                                  copy()                              */
 /************************************************************************/
 
-#ifdef POPPLER_0_58_OR_LATER
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 58
 BaseStream* VSIPDFFileStream::copy()
 {
     return new VSIPDFFileStream(poParent, nStart, bLimited,
                                 nLength, dict.copy());
 }
-#elif defined(POPPLER_0_23_OR_LATER)
+#else
 BaseStream* VSIPDFFileStream::copy()
 {
     return new VSIPDFFileStream(poParent, nStart, bLimited,
@@ -142,7 +136,7 @@
 Stream *VSIPDFFileStream::makeSubStream(makeSubStream_offset_type startA, GBool limitedA,
                                         makeSubStream_offset_type lengthA, makeSubStream_object_type dictA)
 {
-#ifdef POPPLER_0_58_OR_LATER
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 58
     return new VSIPDFFileStream(this,
                                 startA, limitedA,
                                 lengthA, std::move(dictA));
@@ -176,6 +170,9 @@
 /************************************************************************/
 
 StreamKind VSIPDFFileStream::getKind()
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 83
+                                        const
+#endif
 {
     return strFile;
 }
@@ -224,11 +221,12 @@
     // and liberation of VSIPDFFileStream as PDFDoc::str member.
     if( nCurrentPos == 0 || nCurrentPos == VSI_L_OFFSET_MAX )
     {
-        for(int i=0;i<nToRead-(int)strlen("/Linearized ");i++)
+        for(int i=0;i<nBufferLength-(int)strlen("/Linearized ");i++)
         {
             if( memcmp(abyBuffer + i, "/Linearized ",
                        strlen("/Linearized ")) == 0 )
             {
+                bFoundLinearizedHint = true;
                 memcpy(abyBuffer + i, "/XXXXXXXXXX ", strlen("/Linearized "));
                 break;
             }
diff -urN gdal-2.3.2/frmts/pdf/pdfio.h gdal-2.3.2-pdf/frmts/pdf/pdfio.h
--- gdal-2.3.2/frmts/pdf/pdfio.h	2018-09-21 18:04:33.000000000 +0900
+++ gdal-2.3.2-pdf/frmts/pdf/pdfio.h	2019-12-12 12:44:04.922943931 +0900
@@ -1,12 +1,12 @@
 /******************************************************************************
- * $Id: pdfio.h 8f9eb9836e91ff82b8c085c881e978befbbd1f52 2017-09-08 11:23:00Z Even Rouault $
+ * $Id$
  *
  * Project:  PDF driver
  * Purpose:  GDALDataset driver for PDF dataset.
- * Author:   Even Rouault, <even dot rouault at mines dash paris dot org>
+ * Author:   Even Rouault, <even dot rouault at spatialys.com>
  *
  ******************************************************************************
- * Copyright (c) 2010-2013, Even Rouault <even dot rouault at mines-paris dot org>
+ * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -38,27 +38,29 @@
 
 #define BUFFER_SIZE 1024
 
-#ifdef POPPLER_0_23_OR_LATER
 #define getPos_ret_type Goffset
 #define getStart_ret_type Goffset
 #define makeSubStream_offset_type Goffset
 #define setPos_offset_type Goffset
 #define moveStart_delta_type Goffset
-#else
-#define getPos_ret_type int
-#define getStart_ret_type Guint
-#define makeSubStream_offset_type Guint
-#define setPos_offset_type Guint
-#define moveStart_delta_type int
-#endif
 
-#ifdef POPPLER_0_58_OR_LATER
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 58
 #define makeSubStream_object_type Object&&
 #else
 #define makeSubStream_object_type Object*
 #endif
 
-class VSIPDFFileStream: public BaseStream
+// Detect Poppler 0.71 that no longer defines GBool
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 69
+#ifndef initObj
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 71
+#define GBool bool
+#define gFalse false
+#endif
+#endif
+#endif
+
+class VSIPDFFileStream final: public BaseStream
 {
     public:
         VSIPDFFileStream(VSILFILE* f, const char* pszFilename,
@@ -69,9 +71,7 @@
                          makeSubStream_object_type dictA);
         virtual ~VSIPDFFileStream();
 
-#ifdef POPPLER_0_23_OR_LATER
         virtual BaseStream* copy() override;
-#endif
 
         virtual Stream *   makeSubStream(makeSubStream_offset_type startA, GBool limitedA,
                                          makeSubStream_offset_type lengthA, makeSubStream_object_type dictA) override;
@@ -81,7 +81,12 @@
         virtual void       setPos(setPos_offset_type pos, int dir = 0) override;
         virtual void       moveStart(moveStart_delta_type delta) override;
 
-        virtual StreamKind getKind() override;
+        virtual StreamKind getKind()
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 83
+            const
+#endif
+            override;
+
         virtual GooString *getFileName() override;
 
         virtual int        getChar() override;
@@ -92,19 +97,11 @@
         virtual void       unfilteredReset () override;
         virtual void       close() override;
 
+        bool               FoundLinearizedHint() const { return bFoundLinearizedHint; }
+
     private:
-#ifdef POPPLER_BASE_STREAM_HAS_TWO_ARGS
-        /* getChars/hasGetChars added in poppler 0.15.0
-         * POPPLER_BASE_STREAM_HAS_TWO_ARGS true from poppler 0.16,
-         * This test will be wrong for poppler 0.15 or 0.16,
-         * but will still compile correctly.
-         */
         virtual GBool hasGetChars() override;
         virtual int getChars(int nChars, Guchar *buffer) override;
-#else
-        virtual GBool hasGetChars() ;
-        virtual int getChars(int nChars, Guchar *buffer) ;
-#endif
 
         VSIPDFFileStream  *poParent;
         GooString         *poFilename;
@@ -121,6 +118,8 @@
         int                nPosInBuffer;
         int                nBufferLength;
 
+        bool               bFoundLinearizedHint = false;
+
         int                FillBuffer();
 };
 
diff -urN gdal-2.3.2/frmts/pdf/pdfobject.cpp gdal-2.3.2-pdf/frmts/pdf/pdfobject.cpp
--- gdal-2.3.2/frmts/pdf/pdfobject.cpp	2018-09-21 18:04:33.000000000 +0900
+++ gdal-2.3.2-pdf/frmts/pdf/pdfobject.cpp	2019-12-12 14:21:32.751862023 +0900
@@ -2,7 +2,7 @@
  *
  * Project:  PDF driver
  * Purpose:  GDALDataset driver for PDF dataset.
- * Author:   Even Rouault, <even dot rouault at mines dash paris dot org>
+ * Author:   Even Rouault, <even dot rouault at spatialys.com>
  *
  ******************************************************************************
  *
@@ -12,7 +12,7 @@
  * Author: Martin Mikita <martin.mikita@klokantech.com>, xmikit00 @ FIT VUT Brno
  *
  ******************************************************************************
- * Copyright (c) 2011-2013, Even Rouault <even dot rouault at mines-paris dot org>
+ * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -38,7 +38,7 @@
 #include <vector>
 #include "pdfobject.h"
 
-CPL_CVSID("$Id: pdfobject.cpp f1985bbec97048c08fdcb91b7dd7e9c7bec4d36f 2018-05-11 15:50:43 +0200 Even Rouault $")
+CPL_CVSID("$Id$")
 
 /************************************************************************/
 /*                        ROUND_TO_INT_IF_CLOSE()                       */
@@ -247,13 +247,13 @@
 /*                             Serialize()                              */
 /************************************************************************/
 
-void GDALPDFObject::Serialize(CPLString& osStr)
+void GDALPDFObject::Serialize(CPLString& osStr, bool bEmitRef)
 {
-    int nRefNum = GetRefNum();
-    if( nRefNum )
+    auto nRefNum = GetRefNum();
+    if( bEmitRef && nRefNum.toBool() )
     {
         int nRefGen = GetRefGen();
-        osStr.append(CPLSPrintf("%d %d R", nRefNum, nRefGen));
+        osStr.append(CPLSPrintf("%d %d R", nRefNum.toInt(), nRefGen));
         return;
     }
 
@@ -272,11 +272,11 @@
             else if (CanRepresentRealAsString())
             {
                 /* Used for OGC BP numeric values */
-                CPLsnprintf(szReal, sizeof(szReal), "(%.16g)", dfReal);
+                CPLsnprintf(szReal, sizeof(szReal), "(%.*g)", GetPrecision(), dfReal);
             }
             else
             {
-                CPLsnprintf(szReal, sizeof(szReal), "%.16f", dfReal);
+                CPLsnprintf(szReal, sizeof(szReal), "%.*f", GetPrecision(), dfReal);
 
                 /* Remove non significant trailing zeroes */
                 char* pszDot = strchr(szReal, '.');
@@ -312,8 +312,8 @@
 
 GDALPDFObjectRW* GDALPDFObject::Clone()
 {
-    int nRefNum = GetRefNum();
-    if( nRefNum )
+    auto nRefNum = GetRefNum();
+    if( nRefNum.toBool() )
     {
         int nRefGen = GetRefGen();
         return GDALPDFObjectRW::CreateIndirect(nRefNum, nRefGen);
@@ -541,7 +541,7 @@
 /*                            CreateIndirect()                          */
 /************************************************************************/
 
-GDALPDFObjectRW* GDALPDFObjectRW::CreateIndirect(int nNum, int nGen)
+GDALPDFObjectRW* GDALPDFObjectRW::CreateIndirect(const GDALPDFObjectNum& nNum, int nGen)
 {
     GDALPDFObjectRW* poObj = new GDALPDFObjectRW(PDFObjectType_Unknown);
     poObj->m_nNum = nNum;
@@ -594,6 +594,19 @@
 }
 
 /************************************************************************/
+/*                       CreateRealWithPrecision()                      */
+/************************************************************************/
+
+GDALPDFObjectRW* GDALPDFObjectRW::CreateRealWithPrecision(double dfVal,
+                                             int nPrecision)
+{
+    GDALPDFObjectRW* poObj = new GDALPDFObjectRW(PDFObjectType_Real);
+    poObj->m_dfVal = dfVal;
+    poObj->m_nPrecision = nPrecision;
+    return poObj;
+}
+
+/************************************************************************/
 /*                           CreateString()                             */
 /************************************************************************/
 
@@ -740,7 +753,7 @@
 /*                              GetRefNum()                             */
 /************************************************************************/
 
-int GDALPDFObjectRW::GetRefNum()
+GDALPDFObjectNum GDALPDFObjectRW::GetRefNum()
 {
     return m_nNum;
 }
@@ -948,15 +961,19 @@
 class GDALPDFStreamPoppler : public GDALPDFStream
 {
     private:
-        int     m_nLength;
+        int     m_nLength = -1;
         Stream* m_poStream;
+        int     m_nRawLength = -1;
 
     public:
-        GDALPDFStreamPoppler(Stream* poStream) : m_nLength(-1), m_poStream(poStream) {}
+        GDALPDFStreamPoppler(Stream* poStream) : m_poStream(poStream) {}
         virtual ~GDALPDFStreamPoppler() {}
 
         virtual int GetLength() override;
         virtual char* GetBytes() override;
+
+        virtual int GetRawLength() override;
+        virtual char* GetRawBytes() override;
 };
 
 /************************************************************************/
@@ -971,7 +988,7 @@
 
 GDALPDFObjectPoppler::~GDALPDFObjectPoppler()
 {
-#ifndef POPPLER_0_58_OR_LATER
+#if !(POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 58)
     m_po->free();
 #endif
     if (m_bDestroy)
@@ -1055,14 +1072,19 @@
 {
     if (GetType() == PDFObjectType_String)
     {
-#ifdef POPPLER_0_58_OR_LATER
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 58
         // At least available since poppler 0.41
         const GooString* gooString = m_po->getString();
 #else
         GooString* gooString = m_po->getString();
 #endif
+#if (POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 72)
+        return (osStr = GDALPDFGetUTF8StringFromBytes(reinterpret_cast<const GByte*>(gooString->c_str()),
+                                                      static_cast<int>(gooString->getLength())));
+#else
         return (osStr = GDALPDFGetUTF8StringFromBytes(reinterpret_cast<const GByte*>(gooString->getCString()),
                                                       static_cast<int>(gooString->getLength())));
+#endif
     }
     else
         return (osStr = "");
@@ -1137,7 +1159,7 @@
 /*                           SetRefNumAndGen()                          */
 /************************************************************************/
 
-void GDALPDFObjectPoppler::SetRefNumAndGen(int nNum, int nGen)
+void GDALPDFObjectPoppler::SetRefNumAndGen(const GDALPDFObjectNum& nNum, int nGen)
 {
     m_nRefNum = nNum;
     m_nRefGen = nGen;
@@ -1147,7 +1169,7 @@
 /*                               GetRefNum()                            */
 /************************************************************************/
 
-int GDALPDFObjectPoppler::GetRefNum()
+GDALPDFObjectNum GDALPDFObjectPoppler::GetRefNum()
 {
     return m_nRefNum;
 }
@@ -1189,17 +1211,17 @@
     if (oIter != m_map.end())
         return oIter->second;
 
-#ifdef POPPLER_0_58_OR_LATER
-    Object o = m_poDict->lookupNF(((char*)pszKey));
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 58
+    auto&& o(m_poDict->lookupNF(((char*)pszKey)));
     if (!o.isNull())
     {
-        int nRefNum = 0;
+        GDALPDFObjectNum nRefNum;
         int nRefGen = 0;
         if( o.isRef())
         {
             nRefNum = o.getRefNum();
             nRefGen = o.getRefGen();
-            Object o2 = m_poDict->lookup((char*)pszKey);
+            Object o2(m_poDict->lookup((char*)pszKey));
             if( !o2.isNull() )
             {
                 GDALPDFObjectPoppler* poObj = new GDALPDFObjectPoppler(new Object(std::move(o2)), TRUE);
@@ -1210,7 +1232,7 @@
         }
         else
         {
-            GDALPDFObjectPoppler* poObj = new GDALPDFObjectPoppler(new Object(std::move(o)), TRUE);
+            GDALPDFObjectPoppler* poObj = new GDALPDFObjectPoppler(new Object(o.copy()), TRUE);
             poObj->SetRefNumAndGen(nRefNum, nRefGen);
             m_map[pszKey] = poObj;
             return poObj;
@@ -1221,7 +1243,7 @@
     Object* po = new Object;
     if (m_poDict->lookupNF((char*)pszKey, po) && !po->isNull())
     {
-        int nRefNum = 0;
+        GDALPDFObjectNum nRefNum;
         int nRefGen = 0;
         if( po->isRef())
         {
@@ -1323,17 +1345,17 @@
     if (m_v[nIndex] != nullptr)
         return m_v[nIndex];
 
-#ifdef POPPLER_0_58_OR_LATER
-    Object o = m_poArray->getNF(nIndex);
+#if POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 58
+    auto&& o(m_poArray->getNF(nIndex));
     if( !o.isNull() )
     {
-        int nRefNum = 0;
+        GDALPDFObjectNum nRefNum;
         int nRefGen = 0;
         if( o.isRef())
         {
             nRefNum = o.getRefNum();
             nRefGen = o.getRefGen();
-            Object o2 = m_poArray->get(nIndex);
+            Object o2(m_poArray->get(nIndex));
             if( !o2.isNull() )
             {
                 GDALPDFObjectPoppler* poObj = new GDALPDFObjectPoppler(new Object(std::move(o2)), TRUE);
@@ -1344,7 +1366,7 @@
         }
         else
         {
-            GDALPDFObjectPoppler* poObj = new GDALPDFObjectPoppler(new Object(std::move(o)), TRUE);
+            GDALPDFObjectPoppler* poObj = new GDALPDFObjectPoppler(new Object(o.copy()), TRUE);
             poObj->SetRefNumAndGen(nRefNum, nRefGen);
             m_v[nIndex] = poObj;
             return poObj;
@@ -1355,7 +1377,7 @@
     Object* po = new Object;
     if (m_poArray->getNF(nIndex, po))
     {
-        int nRefNum = 0;
+        GDALPDFObjectNum nRefNum;
         int nRefGen = 0;
         if( po->isRef())
         {
@@ -1406,70 +1428,73 @@
 }
 
 /************************************************************************/
-/*                               GetBytes()                             */
+/*                         GooStringToCharStart()                       */
 /************************************************************************/
 
-char* GDALPDFStreamPoppler::GetBytes()
+static char* GooStringToCharStart(GooString& gstr)
 {
-    /* fillGooString() available in poppler >= 0.16.0 */
-#ifdef POPPLER_BASE_STREAM_HAS_TWO_ARGS
-    GooString* gstr = new GooString();
-    m_poStream->fillGooString(gstr);
-
-    if( gstr->getLength() )
+    auto nLength = gstr.getLength();
+    if( nLength )
     {
-        m_nLength = gstr->getLength();
-        char* pszContent = (char*) VSIMalloc(m_nLength + 1);
+        char* pszContent = (char*) VSIMalloc(nLength + 1);
         if (pszContent)
         {
-            memcpy(pszContent, gstr->getCString(), m_nLength);
-            pszContent[m_nLength] = '\0';
-        }
-        delete gstr;
-        return pszContent;
-    }
-    else
-    {
-        delete gstr;
-        return nullptr;
-    }
+#if (POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 72)
+            const char* srcStr = gstr.c_str();
 #else
-    int i;
-    int nLengthAlloc = 0;
-    char* pszContent = nullptr;
-    if( m_nLength >= 0 )
-    {
-        pszContent = (char*) VSIMalloc(m_nLength + 1);
-        if (!pszContent)
-            return nullptr;
-        nLengthAlloc = m_nLength;
-    }
-    m_poStream->reset();
-    for(i = 0; ; ++i )
-    {
-        int nVal = m_poStream->getChar();
-        if (nVal == EOF)
-            break;
-        if( i >= nLengthAlloc )
-        {
-            nLengthAlloc = 32 + nLengthAlloc + nLengthAlloc / 3;
-            char* pszContentNew = (char*) VSIRealloc(pszContent, nLengthAlloc + 1);
-            if( pszContentNew == nullptr )
-            {
-                CPLFree(pszContent);
-                m_nLength = 0;
-                return nullptr;
-            }
-            pszContent = pszContentNew;
+            const char* srcStr = gstr.getCString();
+#endif
+            memcpy(pszContent, srcStr, nLength);
+            pszContent[nLength] = '\0';
         }
-        pszContent[i] = (GByte)nVal;
+        return pszContent;
     }
-    m_nLength = i;
-    pszContent[i] = '\0';
-    return pszContent;
-#endif
+    return nullptr;
+}
+
+/************************************************************************/
+/*                               GetBytes()                             */
+/************************************************************************/
+
+char* GDALPDFStreamPoppler::GetBytes()
+{
+    GooString gstr;
+    m_poStream->fillGooString(&gstr);
+    m_nLength = gstr.getLength();
+    return GooStringToCharStart(gstr);
+}
+
+/************************************************************************/
+/*                            GetRawLength()                            */
+/************************************************************************/
+
+int GDALPDFStreamPoppler::GetRawLength()
+{
+    if (m_nRawLength >= 0)
+        return m_nRawLength;
+
+    auto undecodeStream = m_poStream->getUndecodedStream();
+    undecodeStream->reset();
+    m_nRawLength = 0;
+    while(undecodeStream->getChar() != EOF)
+        m_nRawLength ++;
+    return m_nRawLength;
 }
 
+/************************************************************************/
+/*                             GetRawBytes()                            */
+/************************************************************************/
+
+char* GDALPDFStreamPoppler::GetRawBytes()
+{
+    GooString gstr;
+    auto undecodeStream = m_poStream->getUndecodedStream();
+    undecodeStream->fillGooString(&gstr);
+    m_nRawLength = gstr.getLength();
+    return GooStringToCharStart(gstr);
+}
+
+
 #endif // HAVE_POPPLER
 
 #ifdef HAVE_PODOFO
@@ -1533,6 +1558,9 @@
 
         virtual int GetLength() override;
         virtual char* GetBytes() override;
+
+        virtual int GetRawLength() override;
+        virtual char* GetRawBytes() override;
 };
 
 /************************************************************************/
@@ -1748,9 +1776,9 @@
 /*                               GetRefNum()                            */
 /************************************************************************/
 
-int GDALPDFObjectPodofo::GetRefNum()
+GDALPDFObjectNum GDALPDFObjectPodofo::GetRefNum()
 {
-    return m_po->Reference().ObjectNumber();
+    return GDALPDFObjectNum(m_po->Reference().ObjectNumber());
 }
 
 /************************************************************************/
@@ -1925,6 +1953,51 @@
     return pszContent;
 }
 
+/************************************************************************/
+/*                             GetRawLength()                           */
+/************************************************************************/
+
+int GDALPDFStreamPodofo::GetRawLength()
+{
+    try
+    {
+        auto nLen = m_pStream->GetLength();
+        return (int)nLen;
+    }
+    catch( PoDoFo::PdfError & e )
+    {
+    }
+    return 0;
+}
+
+/************************************************************************/
+/*                              GetRawBytes()                           */
+/************************************************************************/
+
+char* GDALPDFStreamPodofo::GetRawBytes()
+{
+    char* pBuffer = nullptr;
+    PoDoFo::pdf_long nLen = 0;
+    try
+    {
+        m_pStream->GetCopy( &pBuffer, &nLen );
+    }
+    catch( PoDoFo::PdfError & e )
+    {
+        return nullptr;
+    }
+    char* pszContent = (char*) VSIMalloc(nLen + 1);
+    if (!pszContent)
+    {
+        PoDoFo::podofo_free(pBuffer);
+        return nullptr;
+    }
+    memcpy(pszContent, pBuffer, nLen);
+    PoDoFo::podofo_free(pBuffer);
+    pszContent[nLen] = '\0';
+    return pszContent;
+}
+
 #endif // HAVE_PODOFO
 
 #ifdef HAVE_PDFIUM
@@ -1979,18 +2052,24 @@
 {
     private:
         CPDF_Stream* m_pStream;
-        int m_nSize;
-        void* m_pData;
+        int m_nSize = 0;
+        std::unique_ptr<uint8_t, CPLFreeReleaser> m_pData = nullptr;
+        int m_nRawSize = 0;
+        std::unique_ptr<uint8_t, CPLFreeReleaser> m_pRawData = nullptr;
 
         void Decompress();
+        void FillRaw();
 
     public:
         GDALPDFStreamPdfium( CPDF_Stream* pStream ) :
-            m_pStream(pStream), m_nSize(0), m_pData(nullptr) {}
-        virtual ~GDALPDFStreamPdfium() { FX_Free(m_pData); }
+            m_pStream(pStream) {}
+        virtual ~GDALPDFStreamPdfium() {}
 
         virtual int GetLength() override;
         virtual char* GetBytes() override;
+
+        virtual int GetRawLength() override;
+        virtual char* GetRawBytes() override;
 };
 
 /************************************************************************/
@@ -2031,7 +2110,7 @@
 {
     if( poVal == nullptr )
         return nullptr;
-    if( poVal->GetType() == PDFOBJ_REFERENCE )
+    if( poVal->GetType() == CPDF_Object::Type::kReference )
     {
         poVal = poVal->GetDirect();
         if( poVal == nullptr )
@@ -2051,17 +2130,20 @@
 {
     switch(m_po->GetType())
     {
-        case PDFOBJ_NULL:                     return PDFObjectType_Null;
-        case PDFOBJ_BOOLEAN:                  return PDFObjectType_Bool;
-        case PDFOBJ_NUMBER:
+        case CPDF_Object::Type::kNullobj:                  return PDFObjectType_Null;
+        case CPDF_Object::Type::kBoolean:                  return PDFObjectType_Bool;
+        case CPDF_Object::Type::kNumber:
           return (reinterpret_cast<CPDF_Number*>(m_po))->IsInteger()
               ? PDFObjectType_Int
               : PDFObjectType_Real;
-        case PDFOBJ_STRING:                   return PDFObjectType_String;
-        case PDFOBJ_NAME:                     return PDFObjectType_Name;
-        case PDFOBJ_ARRAY:                    return PDFObjectType_Array;
-        case PDFOBJ_DICTIONARY:               return PDFObjectType_Dictionary;
-        case PDFOBJ_STREAM:                   return PDFObjectType_Dictionary;
+        case CPDF_Object::Type::kString:                   return PDFObjectType_String;
+        case CPDF_Object::Type::kName:                     return PDFObjectType_Name;
+        case CPDF_Object::Type::kArray:                    return PDFObjectType_Array;
+        case CPDF_Object::Type::kDictionary:               return PDFObjectType_Dictionary;
+        case CPDF_Object::Type::kStream:                   return PDFObjectType_Dictionary;
+        case CPDF_Object::Type::kReference:
+            // unresolved reference
+            return PDFObjectType_Unknown;
         default:
           CPLAssert(false);
           return PDFObjectType_Unknown;
@@ -2074,7 +2156,7 @@
 
 const char* GDALPDFObjectPdfium::GetTypeNameNative()
 {
-    if(m_po->GetType() == PDFOBJ_STREAM)
+    if(m_po->GetType() == CPDF_Object::Type::kStream)
       return "stream";
     else
       return "";
@@ -2156,11 +2238,11 @@
 const CPLString& GDALPDFObjectPdfium::GetString()
 {
     if (GetType() == PDFObjectType_String) {
-        CFX_ByteStringC bs = m_po->GetConstString();
+        const auto bs = m_po->GetString();
         // If empty string, code crashes
         if(bs.IsEmpty())
           return (osStr = "");
-        return (osStr = GDALPDFGetUTF8StringFromBytes(static_cast<const GByte*>(bs.GetPtr()),
+        return (osStr = GDALPDFGetUTF8StringFromBytes(static_cast<const GByte*>(bs.raw_str()),
                                                       static_cast<int>(bs.GetLength())));
     }
     else
@@ -2174,7 +2256,7 @@
 const CPLString&  GDALPDFObjectPdfium::GetName()
 {
     if (GetType() == PDFObjectType_Name)
-        return (osStr = m_po->GetConstString().GetCStr());
+        return (osStr = m_po->GetString().c_str());
     else
         return (osStr = "");
 }
@@ -2217,7 +2299,7 @@
 
 GDALPDFStream* GDALPDFObjectPdfium::GetStream()
 {
-    if (m_po->GetType() != PDFOBJ_STREAM)
+    if (m_po->GetType() != CPDF_Object::Type::kStream)
         return nullptr;
 
     if (m_poStream)
@@ -2236,9 +2318,9 @@
 /*                               GetRefNum()                            */
 /************************************************************************/
 
-int GDALPDFObjectPdfium::GetRefNum()
+GDALPDFObjectNum GDALPDFObjectPdfium::GetRefNum()
 {
-    return m_po->GetObjNum();
+    return GDALPDFObjectNum(m_po->GetObjNum());
 }
 
 /************************************************************************/
@@ -2278,8 +2360,8 @@
     if (oIter != m_map.end())
         return oIter->second;
 
-    CFX_ByteStringC pdfiumKey(pszKey);
-    CPDF_Object* poVal = m_poDict->GetElement(pdfiumKey);
+    ByteString pdfiumKey(pszKey);
+    CPDF_Object* poVal = m_poDict->GetObjectFor(pdfiumKey);
     GDALPDFObjectPdfium* poObj = GDALPDFObjectPdfium::Build(poVal);
     if (poObj)
     {
@@ -2298,24 +2380,23 @@
 
 std::map<CPLString, GDALPDFObject*>& GDALPDFDictionaryPdfium::GetValues()
 {
-    FX_POSITION pos = m_poDict->GetStartPos();
-    while(pos)
+    CPDF_DictionaryLocker dictIterator(m_poDict);
+    for( const auto iter: dictIterator )
     {
-        CFX_ByteString key;
-        CPDF_Object* poVal = m_poDict->GetNextElement(pos, key);
         // No object for this key
-        if(!poVal)
-          continue;
+        if( !iter.second )
+            continue;
 
-        const char* pszKey = key.c_str();
+        const char* pszKey = iter.first.c_str();
         // Objects exists in the map
         if(m_map.find(pszKey) != m_map.end())
           continue;
-        GDALPDFObjectPdfium* poObj = GDALPDFObjectPdfium::Build(poVal);
+        GDALPDFObjectPdfium* poObj = GDALPDFObjectPdfium::Build(iter.second.Get());
         if( poObj == nullptr )
             continue;
         m_map[pszKey] = poObj;
     }
+
     return m_map;
 }
 
@@ -2339,7 +2420,7 @@
 
 int GDALPDFArrayPdfium::GetLength()
 {
-    return static_cast<int>(m_poArray->GetCount());
+    return static_cast<int>(m_poArray->size());
 }
 
 /************************************************************************/
@@ -2364,7 +2445,7 @@
     if (m_v[nIndex] != nullptr)
         return m_v[nIndex];
 
-    CPDF_Object* poVal = m_poArray->GetElement(nIndex);
+    CPDF_Object* poVal = m_poArray->GetObjectAt(nIndex);
     GDALPDFObjectPdfium* poObj = GDALPDFObjectPdfium::Build(poVal);
     if( poObj == nullptr )
         return nullptr;
@@ -2382,10 +2463,19 @@
 {
     if( m_pData != nullptr )
         return;
-    CPDF_StreamAcc acc;
-    acc.LoadAllData(m_pStream);
-    m_nSize = static_cast<int>(acc.GetSize());
-    m_pData = acc.DetachData();
+    auto acc(pdfium::MakeRetain<CPDF_StreamAcc>(m_pStream));
+    acc->LoadAllDataFiltered();
+    m_nSize = static_cast<int>(acc->GetSize());
+    m_pData.reset();
+    // We don't use m_pData->Detach() as we don't want to deal with
+    // std::unique_ptr<uint8_t, FxFreeDeleter>, and FxFreeDeleter behaviour
+    // depends on whether GDAL and pdfium are compiled with the same
+    // NDEBUG and DCHECK_ALWAYS_ON settings
+    if( m_nSize )
+    {
+        m_pData.reset(static_cast<uint8_t*>(CPLMalloc(m_nSize)));
+        memcpy(&m_pData.get()[0], acc->GetData(), m_nSize);
+    }
 }
 
 /************************************************************************/
@@ -2410,9 +2500,60 @@
     char* pszContent = (char*) VSIMalloc(sizeof(char)*(nLength + 1));
     if (!pszContent)
         return nullptr;
-    memcpy( pszContent, m_pData, nLength);
+    memcpy( pszContent, m_pData.get(), nLength);
     pszContent[nLength] = '\0';
     return pszContent;
 }
 
+/************************************************************************/
+/*                                FillRaw()                             */
+/************************************************************************/
+
+void GDALPDFStreamPdfium::FillRaw()
+{
+    if( m_pRawData != nullptr )
+        return;
+    auto acc(pdfium::MakeRetain<CPDF_StreamAcc>(m_pStream));
+    acc->LoadAllDataRaw();
+    m_nRawSize = static_cast<int>(acc->GetSize());
+    m_pRawData.reset();
+    // We don't use m_pData->Detach() as we don't want to deal with
+    // std::unique_ptr<uint8_t, FxFreeDeleter>, and FxFreeDeleter behaviour
+    // depends on whether GDAL and pdfium are compiled with the same
+    // NDEBUG and DCHECK_ALWAYS_ON settings
+    if( m_nRawSize )
+    {
+        m_pRawData.reset(static_cast<uint8_t*>(CPLMalloc(m_nRawSize)));
+        memcpy(&m_pRawData.get()[0], acc->GetData(), m_nRawSize);
+    }
+}
+
+/************************************************************************/
+/*                            GetRawLength()                            */
+/************************************************************************/
+
+int GDALPDFStreamPdfium::GetRawLength()
+{
+    FillRaw();
+    return m_nRawSize;
+}
+
+/************************************************************************/
+/*                             GetRawBytes()                            */
+/************************************************************************/
+
+char* GDALPDFStreamPdfium::GetRawBytes()
+{
+    int nLength = GetRawLength();
+    if(nLength == 0)
+      return nullptr;
+    char* pszContent = (char*) VSIMalloc(sizeof(char)*(nLength + 1));
+    if (!pszContent)
+        return nullptr;
+    memcpy( pszContent, m_pRawData.get(), nLength);
+    pszContent[nLength] = '\0';
+    return pszContent;
+}
+
+
 #endif // HAVE_PDFIUM
diff -urN gdal-2.3.2/frmts/pdf/pdfobject.h gdal-2.3.2-pdf/frmts/pdf/pdfobject.h
--- gdal-2.3.2/frmts/pdf/pdfobject.h	2018-09-21 18:04:33.000000000 +0900
+++ gdal-2.3.2-pdf/frmts/pdf/pdfobject.h	2019-12-12 12:44:04.922943931 +0900
@@ -1,9 +1,9 @@
 /******************************************************************************
- * $Id: pdfobject.h 7e07230bbff24eb333608de4dbd460b7312839d0 2017-12-11 19:08:47Z Even Rouault $
+ * $Id$
  *
  * Project:  PDF driver
  * Purpose:  GDALDataset driver for PDF dataset.
- * Author:   Even Rouault, <even dot rouault at mines dash paris dot org>
+ * Author:   Even Rouault, <even dot rouault at spatialys.com>
  *
  ******************************************************************************
  *
@@ -13,7 +13,7 @@
  * Author: Martin Mikita <martin.mikita@klokantech.com>, xmikit00 @ FIT VUT Brno
  *
  ******************************************************************************
- * Copyright (c) 2011-2014, Even Rouault <even dot rouault at mines-paris dot org>
+ * Copyright (c) 2011-2014, Even Rouault <even dot rouault at spatialys.com>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -69,6 +69,21 @@
 class GDALPDFDictionaryRW;
 class GDALPDFArrayRW;
 
+class GDALPDFObjectNum
+{
+        int m_nId;
+
+    public:
+        explicit GDALPDFObjectNum(int nId = 0): m_nId(nId) {}
+        GDALPDFObjectNum(const GDALPDFObjectNum& other) = default;
+        GDALPDFObjectNum& operator=(int nId) { m_nId = nId; return *this; }
+
+        int toInt() const { return m_nId; }
+        bool toBool() const { return m_nId > 0; }
+        bool operator==(const GDALPDFObjectNum& other) const { return m_nId == other.m_nId; }
+        bool operator<(const GDALPDFObjectNum& other) const { return m_nId < other.m_nId; }
+};
+
 class GDALPDFObject
 {
     protected:
@@ -88,12 +103,13 @@
         virtual GDALPDFDictionary*  GetDictionary() = 0;
         virtual GDALPDFArray*       GetArray() = 0;
         virtual GDALPDFStream*      GetStream() = 0;
-        virtual int                 GetRefNum() = 0;
+        virtual GDALPDFObjectNum    GetRefNum() = 0;
         virtual int                 GetRefGen() = 0;
+        virtual int                 GetPrecision() const { return 16; }
 
         GDALPDFObject*              LookupObject(const char* pszPath);
 
-        void                        Serialize(CPLString& osStr);
+        void                        Serialize(CPLString& osStr, bool bEmitRef = true);
         CPLString                   Serialize() { CPLString osStr; Serialize(osStr); return osStr; }
         GDALPDFObjectRW*            Clone();
 };
@@ -133,6 +149,9 @@
 
         virtual int GetLength() = 0;
         virtual char* GetBytes() = 0;
+
+        virtual int GetRawLength() = 0;
+        virtual char* GetRawBytes() = 0;
 };
 
 class GDALPDFObjectRW : public GDALPDFObject
@@ -144,9 +163,10 @@
         CPLString             m_osVal;
         GDALPDFDictionaryRW  *m_poDict;
         GDALPDFArrayRW       *m_poArray;
-        int                   m_nNum;
+        GDALPDFObjectNum       m_nNum;
         int                   m_nGen;
         int                   m_bCanRepresentRealAsString;
+        int                   m_nPrecision = 16;
 
         explicit              GDALPDFObjectRW(GDALPDFObjectType eType);
 
@@ -155,11 +175,12 @@
 
     public:
 
-        static GDALPDFObjectRW* CreateIndirect(int nNum, int nGen);
+        static GDALPDFObjectRW* CreateIndirect(const GDALPDFObjectNum& nNum, int nGen);
         static GDALPDFObjectRW* CreateNull();
         static GDALPDFObjectRW* CreateBool(int bVal);
         static GDALPDFObjectRW* CreateInt(int nVal);
         static GDALPDFObjectRW* CreateReal(double dfVal, int bCanRepresentRealAsString = FALSE);
+        static GDALPDFObjectRW* CreateRealWithPrecision(double dfVal, int nPrecision);
         static GDALPDFObjectRW* CreateString(const char* pszStr);
         static GDALPDFObjectRW* CreateName(const char* pszName);
         static GDALPDFObjectRW* CreateDictionary(GDALPDFDictionaryRW* poDict);
@@ -176,8 +197,9 @@
         virtual GDALPDFDictionary*  GetDictionary() override;
         virtual GDALPDFArray*       GetArray() override;
         virtual GDALPDFStream*      GetStream() override;
-        virtual int                 GetRefNum() override;
+        virtual GDALPDFObjectNum    GetRefNum() override;
         virtual int                 GetRefGen() override;
+        virtual int                 GetPrecision() const override { return m_nPrecision; }
 };
 
 class GDALPDFDictionaryRW : public GDALPDFDictionary
@@ -200,7 +222,7 @@
         GDALPDFDictionaryRW&   Add(const char* pszKey, const char* pszVal) { return Add(pszKey, GDALPDFObjectRW::CreateString(pszVal)); }
         GDALPDFDictionaryRW&   Add(const char* pszKey, int nVal) { return Add(pszKey, GDALPDFObjectRW::CreateInt(nVal)); }
         GDALPDFDictionaryRW&   Add(const char* pszKey, double dfVal, int bCanRepresentRealAsString = FALSE) { return Add(pszKey, GDALPDFObjectRW::CreateReal(dfVal, bCanRepresentRealAsString)); }
-        GDALPDFDictionaryRW&   Add(const char* pszKey, int nNum, int nGen) { return Add(pszKey, GDALPDFObjectRW::CreateIndirect(nNum, nGen)); }
+        GDALPDFDictionaryRW&   Add(const char* pszKey, const GDALPDFObjectNum& nNum, int nGen) { return Add(pszKey, GDALPDFObjectRW::CreateIndirect(nNum, nGen)); }
 };
 
 class GDALPDFArrayRW : public GDALPDFArray
@@ -222,8 +244,9 @@
         GDALPDFArrayRW&        Add(const char* pszVal) { return Add(GDALPDFObjectRW::CreateString(pszVal)); }
         GDALPDFArrayRW&        Add(int nVal) { return Add(GDALPDFObjectRW::CreateInt(nVal)); }
         GDALPDFArrayRW&        Add(double dfVal, int bCanRepresentRealAsString = FALSE) { return Add(GDALPDFObjectRW::CreateReal(dfVal, bCanRepresentRealAsString)); }
+        GDALPDFArrayRW&        AddWithPrecision(double dfVal, int nPrecision) { return Add(GDALPDFObjectRW::CreateRealWithPrecision(dfVal, nPrecision)); }
         GDALPDFArrayRW&        Add(double* padfVal, int nCount, int bCanRepresentRealAsString = FALSE);
-        GDALPDFArrayRW&        Add(int nNum, int nGen) { return Add(GDALPDFObjectRW::CreateIndirect(nNum, nGen)); }
+        GDALPDFArrayRW&        Add(const GDALPDFObjectNum& nNum, int nGen) { return Add(GDALPDFObjectRW::CreateIndirect(nNum, nGen)); }
 };
 
 #ifdef HAVE_POPPLER
@@ -237,7 +260,7 @@
         GDALPDFArray* m_poArray;
         GDALPDFStream* m_poStream;
         CPLString osStr;
-        int m_nRefNum;
+        GDALPDFObjectNum m_nRefNum;
         int m_nRefGen;
 
     protected:
@@ -249,7 +272,7 @@
                 m_poDict(nullptr), m_poArray(nullptr), m_poStream(nullptr),
                 m_nRefNum(0), m_nRefGen(0) {}
 
-        void SetRefNumAndGen(int nNum, int nGen);
+        void SetRefNumAndGen(const GDALPDFObjectNum& nNum, int nGen);
 
         virtual ~GDALPDFObjectPoppler();
 
@@ -262,7 +285,7 @@
         virtual GDALPDFDictionary*  GetDictionary() override;
         virtual GDALPDFArray*       GetArray() override;
         virtual GDALPDFStream*      GetStream() override;
-        virtual int                 GetRefNum() override;
+        virtual GDALPDFObjectNum     GetRefNum() override;
         virtual int                 GetRefGen() override;
 };
 
@@ -299,7 +322,7 @@
         virtual GDALPDFDictionary*  GetDictionary() override;
         virtual GDALPDFArray*       GetArray() override;
         virtual GDALPDFStream*      GetStream() override;
-        virtual int                 GetRefNum() override;
+        virtual GDALPDFObjectNum     GetRefNum() override;
         virtual int                 GetRefGen() override;
 };
 
@@ -335,7 +358,7 @@
         virtual GDALPDFDictionary*  GetDictionary() override;
         virtual GDALPDFArray*       GetArray() override;
         virtual GDALPDFStream*      GetStream() override;
-        virtual int                 GetRefNum() override;
+        virtual GDALPDFObjectNum     GetRefNum() override;
         virtual int                 GetRefGen() override;
 };
 
diff -urN gdal-2.3.2/frmts/pdf/pdfreadvectors.cpp gdal-2.3.2-pdf/frmts/pdf/pdfreadvectors.cpp
--- gdal-2.3.2/frmts/pdf/pdfreadvectors.cpp	2018-09-21 18:04:33.000000000 +0900
+++ gdal-2.3.2-pdf/frmts/pdf/pdfreadvectors.cpp	2019-12-12 15:35:55.370214841 +0900
@@ -2,10 +2,10 @@
  *
  * Project:  PDF driver
  * Purpose:  GDALDataset driver for PDF dataset (read vector features)
- * Author:   Even Rouault, <even dot rouault at mines dash paris dot org>
+ * Author:   Even Rouault, <even dot rouault at spatialys.com>
  *
  ******************************************************************************
- * Copyright (c) 2010-2014, Even Rouault <even dot rouault at mines-paris dot org>
+ * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys.com>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -31,9 +31,11 @@
 #define SQUARE(x) ((x)*(x))
 #define EPSILON 1e-5
 
-CPL_CVSID("$Id: pdfreadvectors.cpp acf813919020dfdc36edba989b9f01a4f2a22fc7 2018-06-29 12:11:30 +0200 Even Rouault $")
+CPL_CVSID("$Id$")
 
-#if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) || defined(HAVE_PDFIUM)
+#ifdef HAVE_PDF_READ_SUPPORT
+
+constexpr int BEZIER_STEPS = 10;
 
 /************************************************************************/
 /*                        OpenVectorLayers()                            */
@@ -245,7 +247,7 @@
     if (nRecLevel == 16)
         return;
 
-    std::pair<int,int> oObjPair( poObj->GetRefNum(), poObj->GetRefGen() );
+    std::pair<int,int> oObjPair( poObj->GetRefNum().toInt(), poObj->GetRefGen() );
     if( aoSetAlreadyVisited.find( oObjPair ) != aoSetAlreadyVisited.end() )
         return;
     aoSetAlreadyVisited.insert( oObjPair );
@@ -277,6 +279,7 @@
     {
         GDALPDFArray* poArray = poK->GetArray();
         if (poArray->GetLength() > 0 &&
+            poArray->Get(0) &&
             poArray->Get(0)->GetType() == PDFObjectType_Dictionary &&
             poArray->Get(0)->GetDictionary()->Get("K") != nullptr &&
             poArray->Get(0)->GetDictionary()->Get("K")->GetType() == PDFObjectType_Int)
@@ -292,17 +295,13 @@
                     osLayerName = CPLSPrintf("Layer%d", nLayers + 1);
             }
 
-            const char* l_pszWKT = GetProjectionRef();
-            OGRSpatialReference* poSRS = nullptr;
-            if (l_pszWKT && l_pszWKT[0] != '\0')
-            {
-                poSRS = new OGRSpatialReference();
-                poSRS->importFromWkt(l_pszWKT);
-            }
-
+            auto poSRSOri = GetSpatialRef();
+            OGRSpatialReference* poSRS = poSRSOri ? poSRSOri->Clone() : nullptr;
             OGRPDFLayer* poLayer =
                 new OGRPDFLayer(this, osLayerName.c_str(), poSRS, wkbUnknown);
-            delete poSRS;
+            if( poSRS )
+                delete poSRS;
+                //poSRS->Release();
 
             poLayer->Fill(poArray);
 
@@ -314,8 +313,14 @@
         else
         {
             for(int i=0;i<poArray->GetLength();i++)
-                ExploreTree(poArray->Get(i), aoSetAlreadyVisited,
-                            nRecLevel + 1);
+            {
+                auto poSubObj = poArray->Get(i);
+                if (poSubObj )
+                {
+                    ExploreTree(poSubObj, aoSetAlreadyVisited,
+                                nRecLevel + 1);
+                }
+            }
         }
     }
     else if (poK->GetType() == PDFObjectType_Dictionary)
@@ -432,16 +437,16 @@
 
 static OGRPoint* PDFGetCircleCenter(OGRLineString* poLS)
 {
-    if (poLS == nullptr || poLS->getNumPoints() != 5)
+    if (poLS == nullptr || poLS->getNumPoints() != 1 + 4 * BEZIER_STEPS)
         return nullptr;
 
-    if (poLS->getY(0) == poLS->getY(2) &&
-        poLS->getX(1) == poLS->getX(3) &&
-        fabs((poLS->getX(0) + poLS->getX(2)) / 2 - poLS->getX(1)) < EPSILON &&
-        fabs((poLS->getY(1) + poLS->getY(3)) / 2 - poLS->getY(0)) < EPSILON)
+    if (poLS->getY(0 * BEZIER_STEPS) == poLS->getY(2 * BEZIER_STEPS) &&
+        poLS->getX(1 * BEZIER_STEPS) == poLS->getX(3 * BEZIER_STEPS) &&
+        fabs((poLS->getX(0 * BEZIER_STEPS) + poLS->getX(2 * BEZIER_STEPS)) / 2 - poLS->getX(1 * BEZIER_STEPS)) < EPSILON &&
+        fabs((poLS->getY(1 * BEZIER_STEPS) + poLS->getY(3 * BEZIER_STEPS)) / 2 - poLS->getY(0 * BEZIER_STEPS)) < EPSILON)
     {
-        return new OGRPoint((poLS->getX(0) + poLS->getX(2)) / 2,
-                            (poLS->getY(1) + poLS->getY(3)) / 2);
+        return new OGRPoint((poLS->getX(0 * BEZIER_STEPS) + poLS->getX(2 * BEZIER_STEPS)) / 2,
+                            (poLS->getY(1 * BEZIER_STEPS) + poLS->getY(3 * BEZIER_STEPS)) / 2);
     }
     return nullptr;
 }
@@ -509,6 +514,8 @@
     double dfSqD13 = SQUARE(poLS->getX(1) - poLS->getX(3)) +
                       SQUARE(poLS->getY(1) - poLS->getY(3));
     const double dfSin18divSin126 = 0.38196601125;
+    if( dfSqD02 == 0 )
+        return nullptr;
     int bOK = fabs(dfSqD13 / dfSqD02 - SQUARE(dfSin18divSin126)) < EPSILON;
     for(int i=1;i<10 && bOK;i++)
     {
@@ -563,6 +570,42 @@
 }
 
 /************************************************************************/
+/*                           AddBezierCurve()                           */
+/************************************************************************/
+
+static void AddBezierCurve(std::vector<double>& oCoords,
+                           const double* x0_y0,
+                           const double* x1_y1,
+                           const double* x2_y2,
+                           const double* x3_y3)
+{
+    double x0 = x0_y0[0];
+    double y0 = x0_y0[1];
+    double x1 = x1_y1[0];
+    double y1 = x1_y1[1];
+    double x2 = x2_y2[0];
+    double y2 = x2_y2[1];
+    double x3 = x3_y3[0];
+    double y3 = x3_y3[1];
+    for( int i = 1; i < BEZIER_STEPS; i++ )
+    {
+        const double t = static_cast<double>(i) / BEZIER_STEPS;
+        const double t2 = t * t;
+        const double t3 = t2 * t;
+        const double oneMinust = 1 - t;
+        const double oneMinust2 = oneMinust * oneMinust;
+        const double oneMinust3 = oneMinust2 * oneMinust;
+        const double three_t_oneMinust = 3 * t * oneMinust;
+        const double x = oneMinust3 * x0 + three_t_oneMinust * (oneMinust * x1 + t * x2) + t3 * x3;
+        const double y = oneMinust3 * y0 + three_t_oneMinust * (oneMinust * y1 + t * y2) + t3 * y3;
+        oCoords.push_back(x);
+        oCoords.push_back(y);
+    }
+    oCoords.push_back(x3);
+    oCoords.push_back(y3);
+}
+
+/************************************************************************/
 /*                           ParseContent()                             */
 /************************************************************************/
 
@@ -688,6 +731,54 @@
                 bPushToken = TRUE;
             }
         }
+        else if( bInString && ch == '\\' )
+        {
+            const auto nextCh = pszContent[1];
+            if( nextCh == 'n' )
+            {
+                ADD_CHAR(szToken, '\n');
+                pszContent ++;
+            }
+            else if( nextCh == 'r' )
+            {
+                ADD_CHAR(szToken, '\r');
+                pszContent ++;
+            }
+            else if( nextCh == 't' )
+            {
+                ADD_CHAR(szToken, '\t');
+                pszContent ++;
+            }
+            else if( nextCh == 'b' )
+            {
+                ADD_CHAR(szToken, '\b');
+                pszContent ++;
+            }
+            else if( nextCh == '(' || nextCh == ')' || nextCh == '\\' )
+            {
+                ADD_CHAR(szToken, nextCh);
+                pszContent ++;
+            }
+            else if( nextCh >= '0' && nextCh <= '7' &&
+                     pszContent[2] >= '0' && pszContent[2] <= '7' &&
+                     pszContent[3] >= '0' && pszContent[3] <= '7' )
+            {
+                ADD_CHAR(szToken,
+                         ((nextCh - '\0') * 64 + (pszContent[2] - '\0') * 8 + pszContent[3] - '\0'));
+                pszContent += 3;
+            }
+            else if( nextCh == '\n' )
+            {
+                if( pszContent[2] == '\r' )
+                    pszContent += 2;
+                else
+                    pszContent ++;
+            }
+            else if( nextCh == '\r' )
+            {
+                pszContent ++;
+            }
+        }
         else if (ch == '<' && pszContent[1] == '<' && nTokenSize == 0)
         {
             int nDictDepth = 0;
@@ -953,11 +1044,16 @@
                         return nullptr;
                     }
 
+                    oGS.ApplyMatrix(adfCoords + 0);
+                    oGS.ApplyMatrix(adfCoords + 2);
                     oGS.ApplyMatrix(adfCoords + 4);
-                    oCoords.push_back(adfCoords[4]);
-                    oCoords.push_back(adfCoords[5]);
+                    AddBezierCurve(oCoords,
+                                   oCoords.empty() ? &adfCoords[0] : &oCoords[oCoords.size()-2],
+                                   &adfCoords[0],
+                                   &adfCoords[2],
+                                   &adfCoords[4]);
                 }
-                else if (EQUAL1(szToken, "v") || EQUAL1(szToken, "y")) /* Bezier curve */
+                else if (EQUAL1(szToken, "v")) /* Bezier curve */
                 {
                     double adfCoords[4];
                     if (!UnstackTokens(szToken, 4, aszTokenStack, nTokenStackSize, adfCoords))
@@ -966,9 +1062,30 @@
                         return nullptr;
                     }
 
+                    oGS.ApplyMatrix(adfCoords + 0);
                     oGS.ApplyMatrix(adfCoords + 2);
-                    oCoords.push_back(adfCoords[2]);
-                    oCoords.push_back(adfCoords[3]);
+                    AddBezierCurve(oCoords,
+                                   oCoords.empty() ? &adfCoords[0] : &oCoords[oCoords.size()-2],
+                                   oCoords.empty() ? &adfCoords[0] : &oCoords[oCoords.size()-2],
+                                   &adfCoords[0],
+                                   &adfCoords[2]);
+                }
+                else if (EQUAL1(szToken, "y")) /* Bezier curve */
+                {
+                    double adfCoords[4];
+                    if (!UnstackTokens(szToken, 4, aszTokenStack, nTokenStackSize, adfCoords))
+                    {
+                        CPLDebug("PDF", "Should not happen at line %d", __LINE__);
+                        return nullptr;
+                    }
+
+                    oGS.ApplyMatrix(adfCoords + 0);
+                    oGS.ApplyMatrix(adfCoords + 2);
+                    AddBezierCurve(oCoords,
+                                   oCoords.empty() ? &adfCoords[0] : &oCoords[oCoords.size()-2],
+                                   &adfCoords[0],
+                                   &adfCoords[2],
+                                   &adfCoords[2]);
                 }
                 else if (EQUAL2(szToken, "re")) /* Rectangle */
                 {
@@ -1268,7 +1385,7 @@
 
         // Recognize points as written by GDAL (ogr-sym-2 : circle (not filled))
         OGRGeometry* poCenter = nullptr;
-        if (poCenter == nullptr && poLS != nullptr && poLS->getNumPoints() == 5)
+        if (poCenter == nullptr && poLS != nullptr && poLS->getNumPoints() == 1 + BEZIER_STEPS * 4 )
         {
             poCenter = PDFGetCircleCenter(poLS);
         }
@@ -1335,6 +1452,15 @@
         {
             if (oCoords[i] == NEW_SUBPATH && oCoords[i+1] == NEW_SUBPATH)
             {
+                if (poLS && poLS->getNumPoints() >= 3)
+                {
+                    OGRPolygon* poPoly =  new OGRPolygon();
+                    poPoly->addRingDirectly(poLS);
+                    poLS = nullptr;
+
+                    papoPoly = (OGRGeometry**) CPLRealloc(papoPoly, (nPolys + 1) * sizeof(OGRGeometry*));
+                    papoPoly[nPolys ++] = poPoly;
+                }
                 delete poLS;
                 poLS = new OGRLinearRing();
             }
@@ -1347,16 +1473,21 @@
 
                     OGRPoint* poCenter = nullptr;
 
-                    if (nPolys == 0 &&
+                   if (nPolys == 0 &&
                         poLS &&
-                        poLS->getNumPoints() == 5)
-                    {
+                        poLS->getNumPoints() == 1 + BEZIER_STEPS * 4 )
+                   {
                         // Recognize points as written by GDAL (ogr-sym-3 : circle (filled))
                         poCenter = PDFGetCircleCenter(poLS);
+                   }
 
+                    if (nPolys == 0 &&
+                        poCenter == nullptr &&
+                        poLS &&
+                        poLS->getNumPoints() == 5)
+                    {
                         // Recognize points as written by GDAL (ogr-sym-5: square (filled))
-                        if (poCenter == nullptr)
-                            poCenter = PDFGetSquareCenter(poLS);
+                        poCenter = PDFGetSquareCenter(poLS);
 
                         /* ESRI points */
                         if (poCenter == nullptr &&
@@ -1477,7 +1608,13 @@
     {
         GDALPDFArray* poArray = poObj->GetArray();
         for(int i=0;i<poArray->GetLength();i++)
-            ExploreContents(poArray->Get(i), poResources);
+        {
+            GDALPDFObject* poSubObj = poArray->Get(i);
+            if( poSubObj )
+            {
+                ExploreContents(poSubObj, poResources);
+            }
+        }
     }
 
     if (poObj->GetType() != PDFObjectType_Dictionary)
@@ -1553,7 +1690,7 @@
         for(int i=0;i<poArray->GetLength();i++)
         {
             GDALPDFObject* poObj = poArray->Get(i);
-            if( poObj->GetType() != PDFObjectType_Dictionary)
+            if( poObj == nullptr || poObj->GetType() != PDFObjectType_Dictionary)
                 break;
             GDALPDFStream* poStream = poObj->GetStream();
             if (!poStream)
@@ -1605,40 +1742,20 @@
         if (poProperties != nullptr &&
             poProperties->GetType() == PDFObjectType_Dictionary)
         {
-            char** papszLayersWithRef = osLayerWithRefList.List();
-            char** papszIter = papszLayersWithRef;
             std::map< std::pair<int, int>, OGRPDFLayer *> oMapNumGenToLayer;
-            while(papszIter && *papszIter)
+            for(const auto& oLayerWithref: aoLayerWithRef )
             {
-                char** papszTokens = CSLTokenizeString(*papszIter);
-
-                if( CSLCount(papszTokens) != 3 ) {
-                    CSLDestroy(papszTokens);
-                    CPLDebug("PDF", "Ignore '%s', unparsable.", *papszIter);
-                    papszIter ++;
-                    continue;
-                }
-
-                const char* pszLayerName = papszTokens[0];
-                int nNum = atoi(papszTokens[1]);
-                int nGen = atoi(papszTokens[2]);
-
-                CPLString osSanitizedName(PDFSanitizeLayerName(pszLayerName));
+                CPLString osSanitizedName(PDFSanitizeLayerName(oLayerWithref.osName));
 
                 OGRPDFLayer* poLayer = (OGRPDFLayer*) GetLayerByName(osSanitizedName.c_str());
                 if (poLayer == nullptr)
                 {
-                    const char* l_pszWKT = GetProjectionRef();
-                    OGRSpatialReference* poSRS = nullptr;
-                    if (l_pszWKT && l_pszWKT[0] != '\0')
-                    {
-                        poSRS = new OGRSpatialReference();
-                        poSRS->importFromWkt(l_pszWKT);
-                    }
-
+                    auto poSRSOri = GetSpatialRef();
+                    OGRSpatialReference* poSRS = poSRSOri ? poSRSOri->Clone() : nullptr;
                     poLayer =
                         new OGRPDFLayer(this, osSanitizedName.c_str(), poSRS, wkbUnknown);
-                    delete poSRS;
+                    if( poSRS )
+                        poSRS->Release();
 
                     papoLayers = (OGRLayer**)
                         CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
@@ -1646,10 +1763,7 @@
                     nLayers ++;
                 }
 
-                oMapNumGenToLayer[ std::pair<int,int>(nNum, nGen) ] = poLayer;
-
-                CSLDestroy(papszTokens);
-                papszIter ++;
+                oMapNumGenToLayer[ std::pair<int,int>(oLayerWithref.nOCGNum.toInt(), oLayerWithref.nOCGGen) ] = poLayer;
             }
 
             std::map<CPLString, GDALPDFObject*>& oMap =
@@ -1661,11 +1775,11 @@
             {
                 const char* pszKey = oIter->first.c_str();
                 GDALPDFObject* poObj = oIter->second;
-                if( poObj->GetRefNum() != 0 )
+                if( poObj->GetRefNum().toBool() )
                 {
                     std::map< std::pair<int, int>, OGRPDFLayer *>::iterator
                         oIterNumGenToLayer = oMapNumGenToLayer.find(
-                            std::pair<int,int>(poObj->GetRefNum(), poObj->GetRefGen()) );
+                            std::pair<int,int>(poObj->GetRefNum().toInt(), poObj->GetRefGen()) );
                     if( oIterNumGenToLayer != oMapNumGenToLayer.end() )
                     {
                         oMapPropertyToLayer[pszKey] = oIterNumGenToLayer->second;
@@ -1718,4 +1832,4 @@
     }
 }
 
-#endif /* defined(HAVE_POPPLER) || defined(HAVE_PODOFO) || defined(HAVE_PDFIUM) */
+#endif /* HAVE_PDF_READ_SUPPORT */
diff -urN gdal-2.3.2/frmts/pdf/pdfsdk_headers.h gdal-2.3.2-pdf/frmts/pdf/pdfsdk_headers.h
--- gdal-2.3.2/frmts/pdf/pdfsdk_headers.h	2018-09-21 18:04:33.000000000 +0900
+++ gdal-2.3.2-pdf/frmts/pdf/pdfsdk_headers.h	2019-12-12 12:44:04.923943956 +0900
@@ -50,8 +50,15 @@
 #pragma warning( disable : 4244 ) /* conversion from 'const int' to 'Guchar', possible loss of data */
 #endif
 
+#if !(POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 73)
 #include <goo/gtypes.h>
+#else
+typedef unsigned char Guchar;
+#endif
+
+#if !(POPPLER_MAJOR_VERSION >= 1 || POPPLER_MINOR_VERSION >= 76)
 #include <goo/GooList.h>
+#endif
 
 /* begin of poppler xpdf includes */
 #include <poppler/Object.h>
@@ -114,11 +121,27 @@
 #endif
 
 #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 "public/fpdfview.h"
+#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/page/cpdf_occontext.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fpdfapi/parser/cpdf_object.h"
+#include "core/fpdfapi/parser/cpdf_stream_acc.h"
+#include "core/fpdfapi/render/cpdf_pagerendercontext.h"
+#include "core/fpdfapi/render/cpdf_progressiverenderer.h"
+#include "core/fpdfapi/render/cpdf_rendercontext.h"
+#include "core/fpdfapi/render/cpdf_renderoptions.h"
+#include "core/fpdfdoc/cpdf_annotlist.h"
+#include "core/fxcrt/bytestring.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/cfx_renderdevice.h"
+#include "core/fxge/agg/fx_agg_driver.h"
+#include "core/fxge/renderdevicedriver_iface.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "fpdfsdk/cpdfsdk_pauseadapter.h"
 #endif // HAVE_PDFIUM
 
 #endif
diff -urN gdal-2.3.2/frmts/pdf/pdfwritabledataset.cpp gdal-2.3.2-pdf/frmts/pdf/pdfwritabledataset.cpp
--- gdal-2.3.2/frmts/pdf/pdfwritabledataset.cpp	2018-09-21 18:04:33.000000000 +0900
+++ gdal-2.3.2-pdf/frmts/pdf/pdfwritabledataset.cpp	2019-12-13 15:13:14.245660819 +0900
@@ -2,10 +2,10 @@
  *
  * Project:  PDF driver
  * Purpose:  GDALDataset driver for PDF dataset (writable vector dataset)
- * Author:   Even Rouault, <even dot rouault at mines dash paris dot org>
+ * Author:   Even Rouault, <even dot rouault at spatialys.com>
  *
  ******************************************************************************
- * Copyright (c) 2010-2014, Even Rouault <even dot rouault at mines-paris dot org>
+ * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys.com>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -29,8 +29,9 @@
 #include "gdal_pdf.h"
 #include "pdfcreatecopy.h"
 #include "memdataset.h"
+#include "pdfcreatefromcomposition.h"
 
-CPL_CVSID("$Id: pdfwritabledataset.cpp 22f8ae3bf7bc3cccd970992655c63fc5254d3206 2018-04-08 20:13:05 +0200 Even Rouault $")
+CPL_CVSID("$Id$")
 
 /************************************************************************/
 /*                      PDFWritableVectorDataset()                      */
@@ -62,12 +63,26 @@
 /************************************************************************/
 
 GDALDataset* PDFWritableVectorDataset::Create( const char * pszName,
-                                               CPL_UNUSED int nXSize,
-                                               CPL_UNUSED int nYSize,
+                                               int nXSize,
+                                               int nYSize,
                                                int nBands,
-                                               CPL_UNUSED GDALDataType eType,
+                                               GDALDataType eType,
                                                char ** papszOptions )
 {
+    if( nBands == 0 && nXSize == 0 && nYSize == 0 && eType == GDT_Unknown )
+    {
+        const char* pszFilename = CSLFetchNameValue(papszOptions, "COMPOSITION_FILE");
+        if( pszFilename )
+        {
+            //if( CSLCount(papszOptions) != 1 )
+            {
+                CPLError(CE_Warning, CPLE_AppDefined,
+                         "All others options than COMPOSITION_FILE are ignored");
+            }
+            //return GDALPDFCreateFromCompositionFile(pszName, pszFilename);
+        }
+    }
+
     if( nBands != 0 )
     {
         CPLError(CE_Failure, CPLE_AppDefined,
@@ -96,7 +111,15 @@
 /* -------------------------------------------------------------------- */
 /*      Create the layer object.                                        */
 /* -------------------------------------------------------------------- */
-    OGRLayer* poLayer = new OGRPDFWritableLayer(this, pszLayerName, poSRS, eType);
+    auto poSRSClone = poSRS;
+    if( poSRSClone )
+    {
+        poSRSClone = poSRSClone->Clone();
+        // poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); // Since GDAL 3.0
+    }
+    OGRLayer* poLayer = new OGRPDFWritableLayer(this, pszLayerName, poSRSClone, eType);
+    if( poSRSClone )
+        poSRSClone->Release();
 
     papoLayers = (OGRLayer**)CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
     papoLayers[nLayers] = poLayer;
@@ -171,6 +194,50 @@
         return OGRERR_FAILURE;
     }
 
+    double dfRatio = (sGlobalExtent.MaxY - sGlobalExtent.MinY) / (sGlobalExtent.MaxX - sGlobalExtent.MinX);
+
+    int nWidth, nHeight;
+
+    if (dfRatio < 1)
+    {
+        nWidth = 1024;
+        const double dfHeight = nWidth * dfRatio;
+        if( dfHeight < 1 || dfHeight > INT_MAX || CPLIsNan(dfHeight) )
+        {
+            CPLError(CE_Failure, CPLE_AppDefined, "Invalid image dimensions");
+            return OGRERR_FAILURE;
+        }
+        nHeight = static_cast<int>(dfHeight);
+    }
+    else
+    {
+        nHeight = 1024;
+        const double dfWidth = nHeight / dfRatio;
+        if( dfWidth < 1 || dfWidth > INT_MAX || CPLIsNan(dfWidth) )
+        {
+            CPLError(CE_Failure, CPLE_AppDefined, "Invalid image dimensions");
+            return OGRERR_FAILURE;
+        }
+        nWidth = static_cast<int>(dfWidth);
+    }
+
+    double adfGeoTransform[6];
+    adfGeoTransform[0] = sGlobalExtent.MinX;
+    adfGeoTransform[1] = (sGlobalExtent.MaxX - sGlobalExtent.MinX) / nWidth;
+    adfGeoTransform[2] = 0;
+    adfGeoTransform[3] = sGlobalExtent.MaxY;
+    adfGeoTransform[4] = 0;
+    adfGeoTransform[5] = - (sGlobalExtent.MaxY - sGlobalExtent.MinY) / nHeight;
+
+    // Do again a check against 0, because the above divisions might
+    // transform a difference close to 0, to plain 0.
+    if (adfGeoTransform[1] == 0 || adfGeoTransform[5] == 0)
+    {
+        CPLError(CE_Failure, CPLE_AppDefined,
+                 "Cannot compute spatial extent of features");
+        return OGRERR_FAILURE;
+    }
+
     PDFCompressMethod eStreamCompressMethod = COMPRESS_DEFLATE;
     const char* pszStreamCompressMethod = CSLFetchNameValue(papszOptions, "STREAM_COMPRESS");
     if (pszStreamCompressMethod)
@@ -261,31 +328,8 @@
 
     GDALPDFWriter oWriter(fp);
 
-    double dfRatio = (sGlobalExtent.MaxY - sGlobalExtent.MinY) / (sGlobalExtent.MaxX - sGlobalExtent.MinX);
-
-    int nWidth, nHeight;
-
-    if (dfRatio < 1)
-    {
-        nWidth = 1024;
-        nHeight = static_cast<int>(nWidth * dfRatio);
-    }
-    else
-    {
-        nHeight = 1024;
-        nWidth = static_cast<int>(nHeight / dfRatio);
-    }
-
     GDALDataset* poSrcDS = MEMDataset::Create( "MEM:::", nWidth, nHeight, 0, GDT_Byte, nullptr );
 
-    double adfGeoTransform[6];
-    adfGeoTransform[0] = sGlobalExtent.MinX;
-    adfGeoTransform[1] = (sGlobalExtent.MaxX - sGlobalExtent.MinX) / nWidth;
-    adfGeoTransform[2] = 0;
-    adfGeoTransform[3] = sGlobalExtent.MaxY;
-    adfGeoTransform[4] = 0;
-    adfGeoTransform[5] = - (sGlobalExtent.MaxY - sGlobalExtent.MinY) / nHeight;
-
     poSrcDS->SetGeoTransform(adfGeoTransform);
 
     OGRSpatialReference* poSRS = papoLayers[0]->GetSpatialRef();
diff -urN gdal-2.3.2/nmake.opt gdal-2.3.2-pdf/nmake.opt
--- gdal-2.3.2/nmake.opt	2018-09-21 18:01:50.000000000 +0900
+++ gdal-2.3.2-pdf/nmake.opt	2019-12-12 13:25:10.627693599 +0900
@@ -634,6 +634,8 @@
 # Uncomment POPPLER_0_58_OR_LATER = YES for Poppler >= 0.58.0
 #POPPLER_ENABLED = YES
 #POPPLER_CFLAGS = -Ie:/kde/include -Ie:/kde/include/poppler
+#POPPLER_MAJOR_VERSION = 0
+#POPPLER_MINOR_VERSION = 69
 #POPPLER_HAS_OPTCONTENT = YES
 #POPPLER_BASE_STREAM_HAS_TWO_ARGS = YES
 #POPPLER_0_20_OR_LATER = YES
diff -urN gdal-2.3.2/ogr/ogr_srs_api.h gdal-2.3.2-pdf/ogr/ogr_srs_api.h
--- gdal-2.3.2/ogr/ogr_srs_api.h	2018-09-21 18:05:18.000000000 +0900
+++ gdal-2.3.2-pdf/ogr/ogr_srs_api.h	2019-12-12 13:36:33.257540849 +0900
@@ -652,6 +652,29 @@
                            OGRAxisOrientation eXAxisOrientation,
                            const char *pszYAxisName,
                            OGRAxisOrientation eYAxisOrientation );
+
+#if 0
+== Since Gdal 3.0
+/** Data axis to CRS axis mapping strategy. */
+typedef enum
+{
+    OAMS_TRADITIONAL_GIS_ORDER,  /**< Traditional GIS order */
+    OAMS_AUTHORITY_COMPLIANT,    /**< Compliant with the order mandated by the CRS authority */
+    OAMS_CUSTOM                  /**< Custom */
+} OSRAxisMappingStrategy;
+
+OSRAxisMappingStrategy CPL_DLL OSRGetAxisMappingStrategy( OGRSpatialReferenceH hSRS );
+
+void CPL_DLL OSRSetAxisMappingStrategy( OGRSpatialReferenceH hSRS,
+                                        OSRAxisMappingStrategy strategy );
+
+const int CPL_DLL *OSRGetDataAxisToSRSAxisMapping( OGRSpatialReferenceH hSRS, int* pnCount );
+
+OGRErr CPL_DLL OSRSetDataAxisToSRSAxisMapping( OGRSpatialReferenceH hSRS,
+                                               int nMappingSize,
+                                               const int* panMapping );
+#endif
+
 /** Albers Conic Equal Area */
 OGRErr CPL_DLL OSRSetACEA( OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
                          double dfCenterLat, double dfCenterLong,
diff -urN gdal-2.3.2/port/cpl_string.h gdal-2.3.2-pdf/port/cpl_string.h
--- gdal-2.3.2/port/cpl_string.h	2018-09-21 18:06:33.000000000 +0900
+++ gdal-2.3.2-pdf/port/cpl_string.h	2019-12-12 14:19:19.233463342 +0900
@@ -553,6 +553,13 @@
 /** Unique pointer type to use with CSL functions returning a char** */
 using CSLUniquePtr = std::unique_ptr< char*, CSLDestroyReleaser>;
 
+/*! @cond Doxygen_Suppress */
+struct CPL_DLL CPLFreeReleaser
+{
+    void operator()(void* p) const { CPLFree(p); }
+};
+/*! @endcond */
+
 #endif
 
 } // extern "C++"
