#!/usr/bin/env pytest
# -*- coding: utf-8 -*-
###############################################################################
# $Id: pdf.py 633f02a21c0e3ef2f72103a87e3fba25912c8968 2019-09-25 18:42:02 +0200 Even Rouault $
#
# Project:  GDAL/OGR Test Suite
# Purpose:  PDF Testing.
# Author:   Even Rouault <even.rouault at spatialys.com>
#
###############################################################################
# Copyright (c) 2010-2019, Even Rouault <even.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"),
# 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.
###############################################################################

import os
import sys
from osgeo import gdal
from osgeo import ogr
from osgeo import osr
import pytest

import gdaltest
import ogrtest


pytestmark = pytest.mark.require_driver('PDF')

###############################################################################
# Test driver presence


@pytest.fixture(
    params=['POPPLER', 'PODOFO', 'PDFIUM']
)
def poppler_or_pdfium_or_podofo(request):
    """
    Runs tests with all three backends.
    """
    backend = request.param
    gdaltest.pdf_drv = gdal.GetDriverByName('PDF')

    md = gdaltest.pdf_drv.GetMetadata()
    if 'HAVE_%s' % backend not in md:
        pytest.skip()

    with gdaltest.config_option('GDAL_PDF_LIB', backend):
        yield backend


@pytest.fixture(
    params=['POPPLER', 'PDFIUM']
)
def poppler_or_pdfium(request):
    """
    Runs tests with poppler or pdfium, but not podofo
    """
    backend = request.param
    gdaltest.pdf_drv = gdal.GetDriverByName('PDF')

    md = gdaltest.pdf_drv.GetMetadata()
    if 'HAVE_%s' % backend not in md:
        pytest.skip()

    with gdaltest.config_option('GDAL_PDF_LIB', backend):
        yield backend

###############################################################################
# Returns True if we run with poppler


def pdf_is_poppler():

    md = gdaltest.pdf_drv.GetMetadata()
    val = gdal.GetConfigOption("GDAL_PDF_LIB", "POPPLER")
    if val == 'POPPLER' and 'HAVE_POPPLER' in md:
        return not pdf_is_pdfium()
    return False

###############################################################################
# Returns True if we run with pdfium


def pdf_is_pdfium():

    md = gdaltest.pdf_drv.GetMetadata()
    val = gdal.GetConfigOption("GDAL_PDF_LIB", "PDFIUM")
    if val == 'PDFIUM' and 'HAVE_PDFIUM' in md:
        return True
    return False

###############################################################################
# Returns True if we can compute the checksum


def pdf_checksum_available():

    try:
        ret = gdaltest.pdf_is_checksum_available
        if ret is True or ret is False:
            return ret
    except AttributeError:
        pass

    if pdf_is_poppler() or pdf_is_pdfium():
        gdaltest.pdf_is_checksum_available = True
        return gdaltest.pdf_is_checksum_available

    (_, err) = gdaltest.runexternal_out_and_err("pdftoppm -v")
    if err.startswith('pdftoppm version'):
        gdaltest.pdf_is_checksum_available = True
        return gdaltest.pdf_is_checksum_available
    print('Cannot compute to checksum due to missing pdftoppm')
    print(err)
    gdaltest.pdf_is_checksum_available = False
    return gdaltest.pdf_is_checksum_available

###############################################################################
# Test OGC best practice geospatial PDF


def test_pdf_online_1(poppler_or_pdfium):
    if not gdaltest.download_file('http://www.agc.army.mil/GeoPDFgallery/Imagery/Cherrydale_eDOQQ_1m_0_033_R1C1.pdf', 'Cherrydale_eDOQQ_1m_0_033_R1C1.pdf'):
        pytest.skip()

    try:
        os.stat('tmp/cache/Cherrydale_eDOQQ_1m_0_033_R1C1.pdf')
    except OSError:
        pytest.skip()

    ds = gdal.Open('tmp/cache/Cherrydale_eDOQQ_1m_0_033_R1C1.pdf')
    assert ds is not None

    assert ds.RasterXSize == 1241, 'bad dimensions'

    gt = ds.GetGeoTransform()
    wkt = ds.GetProjectionRef()

    if pdf_is_pdfium():
        expected_gt = (-77.11232757568358, 9.1663393281356228e-06, 0.0, 38.897842406247477, 0.0, -9.1665025563464202e-06)
    elif pdf_is_poppler():
        expected_gt = (-77.112328333299999, 9.1666559999999995e-06, 0.0, 38.897842488372, -0.0, -9.1666559999999995e-06)
    else:
        expected_gt = (-77.112328333299956, 9.1666560000051172e-06, 0.0, 38.897842488371978, 0.0, -9.1666560000046903e-06)

    for i in range(6):
        if gt[i] != pytest.approx(expected_gt[i], abs=1e-15):
            # The remote file has been updated...
            other_expected_gt = (-77.112328333299928, 9.1666560000165691e-06, 0.0, 38.897842488371978, 0.0, -9.1666560000046903e-06)
            for j in range(6):
                assert gt[j] == pytest.approx(other_expected_gt[j], abs=1e-15), 'bad geotransform'

    assert wkt.startswith('GEOGCS["WGS 84"'), 'bad WKT'

    if pdf_checksum_available():
        cs = ds.GetRasterBand(1).Checksum()
        assert cs != 0, 'bad checksum'

    
###############################################################################


def test_pdf_online_2(poppler_or_pdfium):
    try:
        os.stat('tmp/cache/Cherrydale_eDOQQ_1m_0_033_R1C1.pdf')
    except OSError:
        pytest.skip()

    ds = gdal.Open('PDF:1:tmp/cache/Cherrydale_eDOQQ_1m_0_033_R1C1.pdf')
    assert ds is not None

    gt = ds.GetGeoTransform()
    wkt = ds.GetProjectionRef()

    if pdf_is_pdfium():
        expected_gt = (-77.11232757568358, 9.1663393281356228e-06, 0.0, 38.897842406247477, 0.0, -9.1665025563464202e-06)
    elif pdf_is_poppler():
        expected_gt = (-77.112328333299999, 9.1666559999999995e-06, 0.0, 38.897842488372, -0.0, -9.1666559999999995e-06)
    else:
        expected_gt = (-77.112328333299956, 9.1666560000051172e-06, 0.0, 38.897842488371978, 0.0, -9.1666560000046903e-06)

    for i in range(6):
        if gt[i] != pytest.approx(expected_gt[i], abs=1e-15):
            # The remote file has been updated...
            other_expected_gt = (-77.112328333299928, 9.1666560000165691e-06, 0.0, 38.897842488371978, 0.0, -9.1666560000046903e-06)
            for j in range(6):
                assert gt[j] == pytest.approx(other_expected_gt[j], abs=1e-15), 'bad geotransform'

    assert wkt.startswith('GEOGCS["WGS 84"')

###############################################################################
# Test Adobe style geospatial pdf


def test_pdf_1(poppler_or_pdfium):
    gdal.SetConfigOption('GDAL_PDF_DPI', '200')
    ds = gdal.Open('data/adobe_style_geospatial.pdf')
    gdal.SetConfigOption('GDAL_PDF_DPI', None)
    assert ds is not None

    gt = ds.GetGeoTransform()
    wkt = ds.GetProjectionRef()

    if pdf_is_pdfium():
        expected_gt = (333275.12406585668, 31.764450118407499, 0.0, 4940392.1233656602, 0.0, -31.794983670894396)
    else:
        expected_gt = (333274.61654367246, 31.764802242655662, 0.0, 4940391.7593506984, 0.0, -31.794745501708238)

    for i in range(6):
        assert gt[i] == pytest.approx(expected_gt[i], abs=1e-6), 'bad geotransform'

    expected_wkt = 'PROJCS["WGS 84 / UTM zone 20N",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-63],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH]]'
    assert wkt == expected_wkt, 'bad WKT'

    if pdf_checksum_available():
        cs = ds.GetRasterBand(1).Checksum()
        # if cs != 17740 and cs != 19346:
        assert cs != 0, 'bad checksum'

    neatline = ds.GetMetadataItem('NEATLINE')
    got_geom = ogr.CreateGeometryFromWkt(neatline)
    if pdf_is_pdfium():
        expected_geom = ogr.CreateGeometryFromWkt('POLYGON ((338304.28536533244187 4896674.10591614805162,338304.812550922040828 4933414.853961281478405,382774.246895745047368 4933414.855149634182453,382774.983309225703124 4896673.95723026804626,338304.28536533244187 4896674.10591614805162))')
    else:
        expected_geom = ogr.CreateGeometryFromWkt('POLYGON ((338304.150125828920864 4896673.639421294443309,338304.177293475600891 4933414.799376524984837,382774.271384406310972 4933414.546264361590147,382774.767329963855445 4896674.273581005632877,338304.150125828920864 4896673.639421294443309))')

    if ogrtest.check_feature_geometry(got_geom, expected_geom) != 0:
        print(neatline)
        pytest.fail('bad neatline')

    
###############################################################################
# Test write support with ISO32000 geo encoding


def test_pdf_iso32000(poppler_or_pdfium_or_podofo):
    tst = gdaltest.GDALTest('PDF', 'byte.tif', 1, None)
    ret = tst.testCreateCopy(check_minmax=0, check_gt=1, check_srs=True, check_checksum_not_null=pdf_checksum_available())

    return ret

###############################################################################
# Test write support with ISO32000 geo encoding, with DPI=300


def test_pdf_iso32000_dpi_300(poppler_or_pdfium):
    tst = gdaltest.GDALTest('PDF', 'byte.tif', 1, None, options=['DPI=300'])
    ret = tst.testCreateCopy(check_minmax=0, check_gt=1, check_srs=True, check_checksum_not_null=pdf_checksum_available())

    return ret

###############################################################################
# Test write support with OGC_BP geo encoding


def test_pdf_ogcbp(poppler_or_pdfium_or_podofo):
    gdal.SetConfigOption('GDAL_PDF_OGC_BP_WRITE_WKT', 'FALSE')
    tst = gdaltest.GDALTest('PDF', 'byte.tif', 1, None, options=['GEO_ENCODING=OGC_BP'])
    ret = tst.testCreateCopy(check_minmax=0, check_gt=1, check_srs=True, check_checksum_not_null=pdf_checksum_available())
    gdal.SetConfigOption('GDAL_PDF_OGC_BP_WRITE_WKT', None)

    return ret

###############################################################################
# Test write support with OGC_BP geo encoding, with DPI=300


def test_pdf_ogcbp_dpi_300(poppler_or_pdfium):
    gdal.SetConfigOption('GDAL_PDF_OGC_BP_WRITE_WKT', 'FALSE')
    tst = gdaltest.GDALTest('PDF', 'byte.tif', 1, None, options=['GEO_ENCODING=OGC_BP', 'DPI=300'])
    ret = tst.testCreateCopy(check_minmax=0, check_gt=1, check_srs=True, check_checksum_not_null=pdf_checksum_available())
    gdal.SetConfigOption('GDAL_PDF_OGC_BP_WRITE_WKT', None)

    return ret


def test_pdf_ogcbp_lcc(poppler_or_pdfium):
    wkt = """PROJCS["NAD83 / Utah North",
    GEOGCS["NAD83",
        DATUM["North_American_Datum_1983",
            SPHEROID["GRS 1980",6378137,298.257222101,
                AUTHORITY["EPSG","7019"]],
            TOWGS84[0,0,0,0,0,0,0]],
        PRIMEM["Greenwich",0],
        UNIT["degree",0.0174532925199433]],
    PROJECTION["Lambert_Conformal_Conic_2SP"],
    PARAMETER["standard_parallel_1",41.78333333333333],
    PARAMETER["standard_parallel_2",40.71666666666667],
    PARAMETER["latitude_of_origin",40.33333333333334],
    PARAMETER["central_meridian",-111.5],
    PARAMETER["false_easting",500000],
    PARAMETER["false_northing",1000000],
    UNIT["metre",1]]]"""

    src_ds = gdal.GetDriverByName('GTiff').Create('tmp/temp.tif', 1, 1)
    src_ds.SetProjection(wkt)
    src_ds.SetGeoTransform([500000, 1, 0, 1000000, 0, -1])

    gdal.SetConfigOption('GDAL_PDF_OGC_BP_WRITE_WKT', 'FALSE')
    out_ds = gdaltest.pdf_drv.CreateCopy('tmp/pdf_ogcbp_lcc.pdf', src_ds)
    out_wkt = out_ds.GetProjectionRef()
    out_ds = None
    gdal.SetConfigOption('GDAL_PDF_OGC_BP_WRITE_WKT', None)

    src_ds = None

    gdal.Unlink('tmp/temp.tif')
    gdal.GetDriverByName('PDF').Delete('tmp/pdf_ogcbp_lcc.pdf')

    sr1 = osr.SpatialReference(wkt)
    sr2 = osr.SpatialReference(out_wkt)
    if sr1.IsSame(sr2) == 0:
        print(sr2.ExportToPrettyWkt())
        pytest.fail('wrong wkt')

    
###############################################################################
# Test no compression


def test_pdf_no_compression(poppler_or_pdfium):
    tst = gdaltest.GDALTest('PDF', 'byte.tif', 1, None, options=['COMPRESS=NONE'])
    ret = tst.testCreateCopy(check_minmax=0, check_gt=0, check_srs=None, check_checksum_not_null=pdf_checksum_available())

    return ret

###############################################################################
# Test compression methods


def _test_pdf_jpeg_compression(filename):
    if gdal.GetDriverByName('JPEG') is None:
        pytest.skip()

    tst = gdaltest.GDALTest('PDF', filename, 1, None, options=['COMPRESS=JPEG'])
    tst.testCreateCopy(check_minmax=0, check_gt=0, check_srs=None, check_checksum_not_null=pdf_checksum_available())


def test_pdf_jpeg_compression(poppler_or_pdfium):
    _test_pdf_jpeg_compression('byte.tif')


def pdf_get_J2KDriver(drv_name):
    drv = gdal.GetDriverByName(drv_name)
    if drv is None:
        return None
    if drv_name == 'JP2ECW':
        import ecw
        if not ecw.has_write_support():
            return None
    return drv


def pdf_jpx_compression(filename, drv_name=None):
    if drv_name is None:
        if pdf_get_J2KDriver('JP2KAK') is None and \
           pdf_get_J2KDriver('JP2ECW') is None and \
           pdf_get_J2KDriver('JP2OpenJpeg') is None and \
           pdf_get_J2KDriver('JPEG2000') is None:
            pytest.skip()
    elif pdf_get_J2KDriver(drv_name) is None:
        pytest.skip()

    if drv_name is None:
        options = ['COMPRESS=JPEG2000']
    else:
        options = ['COMPRESS=JPEG2000', 'JPEG2000_DRIVER=%s' % drv_name]

    tst = gdaltest.GDALTest('PDF', filename, 1, None, options=options)
    ret = tst.testCreateCopy(check_minmax=0, check_gt=0, check_srs=None, check_checksum_not_null=pdf_checksum_available())

    return ret


def test_pdf_jp2_auto_compression(poppler_or_pdfium):
    return pdf_jpx_compression('utm.tif')


def test_pdf_jp2kak_compression(poppler_or_pdfium):
    return pdf_jpx_compression('utm.tif', 'JP2KAK')


def test_pdf_jp2ecw_compression(poppler_or_pdfium):
    return pdf_jpx_compression('utm.tif', 'JP2ECW')


def test_pdf_jp2openjpeg_compression(poppler_or_pdfium):
    return pdf_jpx_compression('utm.tif', 'JP2OpenJpeg')


def test_pdf_jpeg2000_compression(poppler_or_pdfium):
    return pdf_jpx_compression('utm.tif', 'JPEG2000')


def test_pdf_jp2ecw_compression_rgb(poppler_or_pdfium):
    return pdf_jpx_compression('rgbsmall.tif', 'JP2ECW')


def test_pdf_jpeg_compression_rgb(poppler_or_pdfium):
    return _test_pdf_jpeg_compression('rgbsmall.tif')

###############################################################################
# Test RGBA


def pdf_rgba_default_compression(options_param=None):
    options_param = [] if options_param is None else options_param
    if not pdf_checksum_available():
        pytest.skip()

    src_ds = gdal.Open('../gcore/data/stefan_full_rgba.tif')
    out_ds = gdaltest.pdf_drv.CreateCopy('tmp/rgba.pdf', src_ds, options=options_param)
    out_ds = None

    # gdal.SetConfigOption('GDAL_PDF_BANDS', '4')
    gdal.SetConfigOption('PDF_DUMP_OBJECT', 'tmp/rgba.pdf.txt')
    gdal.SetConfigOption('PDF_DUMP_PARENT', 'YES')
    out_ds = gdal.Open('tmp/rgba.pdf')
    gdal.SetConfigOption('PDF_DUMP_OBJECT', None)
    gdal.SetConfigOption('PDF_DUMP_PARENT', None)
    content = open('tmp/rgba.pdf.txt', 'rt').read()
    os.unlink('tmp/rgba.pdf.txt')
    cs1 = out_ds.GetRasterBand(1).Checksum()
    cs2 = out_ds.GetRasterBand(2).Checksum()
    cs3 = out_ds.GetRasterBand(3).Checksum()
    if out_ds.RasterCount == 4:
        cs4 = out_ds.GetRasterBand(4).Checksum()
    else:
        cs4 = -1

    src_cs1 = src_ds.GetRasterBand(1).Checksum()
    src_cs2 = src_ds.GetRasterBand(2).Checksum()
    src_cs3 = src_ds.GetRasterBand(3).Checksum()
    src_cs4 = src_ds.GetRasterBand(4).Checksum()
    out_ds = None
    # gdal.SetConfigOption('GDAL_PDF_BANDS', None)

    gdal.GetDriverByName('PDF').Delete('tmp/rgba.pdf')

    if cs4 < 0:
        pytest.skip()

    assert (content.startswith('Type = dictionary, Num = 3, Gen = 0') and \
       '      Type = dictionary, Num = 3, Gen = 0' in content), \
        'wrong object dump'

    assert cs4 != 0, 'wrong checksum'

    if cs1 != src_cs1 or cs2 != src_cs2 or cs3 != src_cs3 or cs4 != src_cs4:
        print(cs1)
        print(cs2)
        print(cs3)
        print(cs4)
        print(src_cs1)
        print(src_cs2)
        print(src_cs3)
        print(src_cs4)

    

def test_pdf_rgba_default_compression_tiled(poppler_or_pdfium_or_podofo):
    return pdf_rgba_default_compression(['BLOCKXSIZE=32', 'BLOCKYSIZE=32'])


def test_pdf_jpeg_compression_rgba(poppler_or_pdfium):
    return _test_pdf_jpeg_compression('../../gcore/data/stefan_full_rgba.tif')

###############################################################################
# Test PREDICTOR=2


def test_pdf_predictor_2(poppler_or_pdfium):
    tst = gdaltest.GDALTest('PDF', 'utm.tif', 1, None, options=['PREDICTOR=2'])
    ret = tst.testCreateCopy(check_minmax=0, check_gt=0, check_srs=None, check_checksum_not_null=pdf_checksum_available())

    return ret


def test_pdf_predictor_2_rgb(poppler_or_pdfium):
    tst = gdaltest.GDALTest('PDF', 'rgbsmall.tif', 1, None, options=['PREDICTOR=2'])
    ret = tst.testCreateCopy(check_minmax=0, check_gt=0, check_srs=None, check_checksum_not_null=pdf_checksum_available())

    return ret

###############################################################################
# Test tiling


def test_pdf_tiled(poppler_or_pdfium):
    tst = gdaltest.GDALTest('PDF', 'utm.tif', 1, None, options=['COMPRESS=DEFLATE', 'TILED=YES'])
    ret = tst.testCreateCopy(check_minmax=0, check_gt=0, check_srs=None, check_checksum_not_null=pdf_checksum_available())

    return ret


def test_pdf_tiled_128(poppler_or_pdfium):
    tst = gdaltest.GDALTest('PDF', 'utm.tif', 1, None, options=['BLOCKXSIZE=128', 'BLOCKYSIZE=128'])
    ret = tst.testCreateCopy(check_minmax=0, check_gt=0, check_srs=None, check_checksum_not_null=pdf_checksum_available())

    return ret

###############################################################################
# Test raster with color table


def test_pdf_color_table(poppler_or_pdfium):
    if gdal.GetDriverByName('GIF') is None:
        pytest.skip()

    tst = gdaltest.GDALTest('PDF', 'bug407.gif', 1, None)
    ret = tst.testCreateCopy(check_minmax=0, check_gt=0, check_srs=None, check_checksum_not_null=pdf_checksum_available())

    return ret

###############################################################################
# Test XMP support


def test_pdf_xmp(poppler_or_pdfium):
    src_ds = gdal.Open('data/adobe_style_geospatial_with_xmp.pdf')
    gdaltest.pdf_drv.CreateCopy('tmp/pdf_xmp.pdf', src_ds, options=['WRITE_INFO=NO'])
    out_ds = gdal.Open('tmp/pdf_xmp.pdf')
    if out_ds is None:
        # Some Poppler versions cannot re-open the file
        gdal.GetDriverByName('PDF').Delete('tmp/pdf_xmp.pdf')
        pytest.skip()

    ref_md = src_ds.GetMetadata('xml:XMP')
    got_md = out_ds.GetMetadata('xml:XMP')
    base_md = out_ds.GetMetadata()
    out_ds = None
    src_ds = None

    gdal.GetDriverByName('PDF').Delete('tmp/pdf_xmp.pdf')

    assert ref_md[0] == got_md[0]

    assert len(base_md) == 2

###############################################################################
# Test Info


def test_pdf_info(poppler_or_pdfium):
    try:
        val = '\xc3\xa9'.decode('UTF-8')
    except:
        val = '\u00e9'

    options = [
        'AUTHOR=%s' % val,
        'CREATOR=creator',
        'KEYWORDS=keywords',
        'PRODUCER=producer',
        'SUBJECT=subject',
        'TITLE=title'
    ]

    src_ds = gdal.Open('data/byte.tif')
    out_ds = gdaltest.pdf_drv.CreateCopy('tmp/pdf_info.pdf', src_ds, options=options)
    # print(out_ds.GetMetadata())
    out_ds2 = gdaltest.pdf_drv.CreateCopy('tmp/pdf_info_2.pdf', out_ds)
    md = out_ds2.GetMetadata()
    # print(md)
    out_ds2 = None
    out_ds = None
    src_ds = None

    gdal.GetDriverByName('PDF').Delete('tmp/pdf_info.pdf')
    gdal.GetDriverByName('PDF').Delete('tmp/pdf_info_2.pdf')

    assert (md['AUTHOR'] == val and \
       md['CREATOR'] == 'creator' and \
       md['KEYWORDS'] == 'keywords' and \
       md['PRODUCER'] == 'producer' and \
       md['SUBJECT'] == 'subject' and \
       md['TITLE'] == 'title'), "metadata doesn't match"

###############################################################################
# Check SetGeoTransform() / SetProjection()


def test_pdf_update_gt(poppler_or_pdfium_or_podofo):
    src_ds = gdal.Open('data/byte.tif')
    ds = gdaltest.pdf_drv.CreateCopy('tmp/pdf_update_gt.pdf', src_ds)
    ds = None
    src_ds = None

    # Alter geotransform
    ds = gdal.Open('tmp/pdf_update_gt.pdf', gdal.GA_Update)
    sr = osr.SpatialReference()
    sr.ImportFromEPSG(4326)
    ds.SetProjection(sr.ExportToWkt())
    ds.SetGeoTransform([2, 1, 0, 49, 0, -1])
    ds = None

    assert not os.path.exists('tmp/pdf_update_gt.pdf.aux.xml')

    # Check geotransform
    ds = gdal.Open('tmp/pdf_update_gt.pdf')
    gt = ds.GetGeoTransform()
    ds = None

    expected_gt = [2, 1, 0, 49, 0, -1]
    for i in range(6):
        assert gt[i] == pytest.approx(expected_gt[i], abs=1e-8), 'did not get expected gt'

    # Clear geotransform
    ds = gdal.Open('tmp/pdf_update_gt.pdf', gdal.GA_Update)
    ds.SetProjection("")
    ds = None

    # Check geotransform
    ds = gdal.Open('tmp/pdf_update_gt.pdf')
    gt = ds.GetGeoTransform()
    ds = None

    expected_gt = [0.0, 1.0, 0.0, 0.0, 0.0, 1.0]
    for i in range(6):
        assert gt[i] == pytest.approx(expected_gt[i], abs=1e-8), 'did not get expected gt'

    # Set geotransform again
    ds = gdal.Open('tmp/pdf_update_gt.pdf', gdal.GA_Update)
    ds.SetProjection(sr.ExportToWkt())
    ds.SetGeoTransform([3, 1, 0, 50, 0, -1])
    ds = None

    # Check geotransform
    ds = gdal.Open('tmp/pdf_update_gt.pdf')
    gt = ds.GetGeoTransform()
    ds = None

    expected_gt = [3, 1, 0, 50, 0, -1]
    for i in range(6):
        assert gt[i] == pytest.approx(expected_gt[i], abs=1e-8), 'did not get expected gt'

    gdaltest.pdf_drv.Delete('tmp/pdf_update_gt.pdf')

###############################################################################
# Check SetMetadataItem() for Info


def test_pdf_update_info(poppler_or_pdfium_or_podofo):
    src_ds = gdal.Open('data/byte.tif')
    ds = gdaltest.pdf_drv.CreateCopy('tmp/pdf_update_info.pdf', src_ds)
    ds = None
    src_ds = None

    # Add info
    ds = gdal.Open('tmp/pdf_update_info.pdf', gdal.GA_Update)
    ds.SetMetadataItem('AUTHOR', 'author')
    ds = None

    # Check
    ds = gdal.Open('tmp/pdf_update_info.pdf')
    author = ds.GetMetadataItem('AUTHOR')
    ds = None

    assert author == 'author', 'did not get expected metadata'

    # Update info
    ds = gdal.Open('tmp/pdf_update_info.pdf', gdal.GA_Update)
    ds.SetMetadataItem('AUTHOR', 'author2')
    ds = None

    # Check
    ds = gdal.Open('tmp/pdf_update_info.pdf')
    author = ds.GetMetadataItem('AUTHOR')
    ds = None

    assert author == 'author2', 'did not get expected metadata'

    # Clear info
    ds = gdal.Open('tmp/pdf_update_info.pdf', gdal.GA_Update)
    ds.SetMetadataItem('AUTHOR', None)
    ds = None

    # Check PAM doesn't exist
    if os.path.exists('tmp/pdf_update_info.pdf.aux.xml'):
        print(author)
        pytest.fail('did not expected .aux.xml')

    # Check
    ds = gdal.Open('tmp/pdf_update_info.pdf')
    author = ds.GetMetadataItem('AUTHOR')
    ds = None

    assert author is None, 'did not get expected metadata'

    gdaltest.pdf_drv.Delete('tmp/pdf_update_info.pdf')

###############################################################################
# Check SetMetadataItem() for xml:XMP


def test_pdf_update_xmp(poppler_or_pdfium_or_podofo):
    src_ds = gdal.Open('data/byte.tif')
    ds = gdaltest.pdf_drv.CreateCopy('tmp/pdf_update_xmp.pdf', src_ds)
    ds = None
    src_ds = None

    # Add info
    ds = gdal.Open('tmp/pdf_update_xmp.pdf', gdal.GA_Update)
    ds.SetMetadata(["<?xpacket begin='a'/><a/>"], "xml:XMP")
    ds = None

    # Check
    ds = gdal.Open('tmp/pdf_update_xmp.pdf')
    xmp = ds.GetMetadata('xml:XMP')[0]
    ds = None

    assert xmp == "<?xpacket begin='a'/><a/>", 'did not get expected metadata'

    # Update info
    ds = gdal.Open('tmp/pdf_update_xmp.pdf', gdal.GA_Update)
    ds.SetMetadata(["<?xpacket begin='a'/><a_updated/>"], "xml:XMP")
    ds = None

    # Check
    ds = gdal.Open('tmp/pdf_update_xmp.pdf')
    xmp = ds.GetMetadata('xml:XMP')[0]
    ds = None

    assert xmp == "<?xpacket begin='a'/><a_updated/>", 'did not get expected metadata'

    # Check PAM doesn't exist
    assert not os.path.exists('tmp/pdf_update_xmp.pdf.aux.xml'), \
        'did not expected .aux.xml'

    # Clear info
    ds = gdal.Open('tmp/pdf_update_xmp.pdf', gdal.GA_Update)
    ds.SetMetadata(None, "xml:XMP")
    ds = None

    # Check
    ds = gdal.Open('tmp/pdf_update_xmp.pdf')
    xmp = ds.GetMetadata('xml:XMP')
    ds = None

    assert xmp is None, 'did not get expected metadata'

    gdaltest.pdf_drv.Delete('tmp/pdf_update_xmp.pdf')

###############################################################################
# Check SetGCPs() but with GCPs that resolve to a geotransform


def _pdf_update_gcps(poppler_or_pdfium):
    dpi = 300
    out_filename = 'tmp/pdf_update_gcps.pdf'

    src_ds = gdal.Open('data/byte.tif')
    src_wkt = src_ds.GetProjectionRef()
    src_gt = src_ds.GetGeoTransform()
    ds = gdaltest.pdf_drv.CreateCopy(out_filename, src_ds, options=['GEO_ENCODING=NONE', 'DPI=%d' % dpi])
    ds = None
    src_ds = None

    gcp = [[2., 8., 0, 0],
           [2., 18., 0, 0],
           [16., 18., 0, 0],
           [16., 8., 0, 0]]

    for i in range(4):
        gcp[i][2] = src_gt[0] + gcp[i][0] * src_gt[1] + gcp[i][1] * src_gt[2]
        gcp[i][3] = src_gt[3] + gcp[i][0] * src_gt[4] + gcp[i][1] * src_gt[5]

    vrt_txt = """<VRTDataset rasterXSize="20" rasterYSize="20">
<GCPList Projection=''>
    <GCP Id="" Pixel="%f" Line="%f" X="%f" Y="%f"/>
    <GCP Id="" Pixel="%f" Line="%f" X="%f" Y="%f"/>
    <GCP Id="" Pixel="%f" Line="%f" X="%f" Y="%f"/>
    <GCP Id="" Pixel="%f" Line="%f" X="%f" Y="%f"/>
</GCPList>
<VRTRasterBand dataType="Byte" band="1">
    <SimpleSource>
    <SourceFilename relativeToVRT="1">data/byte.tif</SourceFilename>
    <SourceProperties RasterXSize="20" RasterYSize="20" DataType="Byte" BlockXSize="20" BlockYSize="20" />
    <SourceBand>1</SourceBand>
    </SimpleSource>
</VRTRasterBand>
</VRTDataset>""" % (gcp[0][0], gcp[0][1], gcp[0][2], gcp[0][3],
                    gcp[1][0], gcp[1][1], gcp[1][2], gcp[1][3],
                    gcp[2][0], gcp[2][1], gcp[2][2], gcp[2][3],
                    gcp[3][0], gcp[3][1], gcp[3][2], gcp[3][3])
    vrt_ds = gdal.Open(vrt_txt)
    gcps = vrt_ds.GetGCPs()
    vrt_ds = None

    # Set GCPs()
    ds = gdal.Open(out_filename, gdal.GA_Update)
    ds.SetGCPs(gcps, src_wkt)
    ds = None

    # Check
    ds = gdal.Open(out_filename)
    got_gt = ds.GetGeoTransform()
    got_wkt = ds.GetProjectionRef()
    got_gcp_count = ds.GetGCPCount()
    ds.GetGCPs()
    got_gcp_wkt = ds.GetGCPProjection()
    got_neatline = ds.GetMetadataItem('NEATLINE')

    if pdf_is_pdfium():
        max_error = 1
    else:
        max_error = 0.0001

    ds = None

    assert got_wkt != '', 'did not expect null GetProjectionRef'

    assert got_gcp_wkt == '', 'did not expect non null GetGCPProjection'

    for i in range(6):
        assert got_gt[i] == pytest.approx(src_gt[i], abs=1e-8), 'did not get expected gt'

    assert got_gcp_count == 0, 'did not expect GCPs'

    got_geom = ogr.CreateGeometryFromWkt(got_neatline)
    expected_lr = ogr.Geometry(ogr.wkbLinearRing)
    for i in range(4):
        expected_lr.AddPoint_2D(gcp[i][2], gcp[i][3])
    expected_lr.AddPoint_2D(gcp[0][2], gcp[0][3])
    expected_geom = ogr.Geometry(ogr.wkbPolygon)
    expected_geom.AddGeometry(expected_lr)

    if ogrtest.check_feature_geometry(got_geom, expected_geom, max_error=max_error) != 0:
        print('got : %s' % got_neatline)
        print('expected : %s' % expected_geom.ExportToWkt())
        pytest.fail('bad neatline')

    gdaltest.pdf_drv.Delete(out_filename)


def test_pdf_update_gcps_iso32000(poppler_or_pdfium):
    gdal.SetConfigOption('GDAL_PDF_GEO_ENCODING', None)
    _pdf_update_gcps(poppler_or_pdfium)


def test_pdf_update_gcps_ogc_bp(poppler_or_pdfium):
    gdal.SetConfigOption('GDAL_PDF_GEO_ENCODING', 'OGC_BP')
    _pdf_update_gcps(poppler_or_pdfium)
    gdal.SetConfigOption('GDAL_PDF_GEO_ENCODING', None)

###############################################################################
# Check SetGCPs() but with GCPs that do *not* resolve to a geotransform


def test_pdf_set_5_gcps_ogc_bp(poppler_or_pdfium):
    dpi = 300
    out_filename = 'tmp/pdf_set_5_gcps_ogc_bp.pdf'

    src_ds = gdal.Open('data/byte.tif')
    src_wkt = src_ds.GetProjectionRef()
    src_gt = src_ds.GetGeoTransform()
    src_ds = None

    gcp = [[2., 8., 0, 0],
           [2., 10., 0, 0],
           [2., 18., 0, 0],
           [16., 18., 0, 0],
           [16., 8., 0, 0]]

    for i, _ in enumerate(gcp):
        gcp[i][2] = src_gt[0] + gcp[i][0] * src_gt[1] + gcp[i][1] * src_gt[2]
        gcp[i][3] = src_gt[3] + gcp[i][0] * src_gt[4] + gcp[i][1] * src_gt[5]

    # That way, GCPs will not resolve to a geotransform
    gcp[1][2] -= 100

    vrt_txt = """<VRTDataset rasterXSize="20" rasterYSize="20">
<GCPList Projection='%s'>
    <GCP Id="" Pixel="%f" Line="%f" X="%f" Y="%f"/>
    <GCP Id="" Pixel="%f" Line="%f" X="%f" Y="%f"/>
    <GCP Id="" Pixel="%f" Line="%f" X="%f" Y="%f"/>
    <GCP Id="" Pixel="%f" Line="%f" X="%f" Y="%f"/>
    <GCP Id="" Pixel="%f" Line="%f" X="%f" Y="%f"/>
</GCPList>
<VRTRasterBand dataType="Byte" band="1">
    <SimpleSource>
    <SourceFilename relativeToVRT="1">data/byte.tif</SourceFilename>
    <SourceProperties RasterXSize="20" RasterYSize="20" DataType="Byte" BlockXSize="20" BlockYSize="20" />
    <SourceBand>1</SourceBand>
    </SimpleSource>
</VRTRasterBand>
</VRTDataset>""" % (src_wkt,
                    gcp[0][0], gcp[0][1], gcp[0][2], gcp[0][3],
                    gcp[1][0], gcp[1][1], gcp[1][2], gcp[1][3],
                    gcp[2][0], gcp[2][1], gcp[2][2], gcp[2][3],
                    gcp[3][0], gcp[3][1], gcp[3][2], gcp[3][3],
                    gcp[4][0], gcp[4][1], gcp[4][2], gcp[4][3])
    vrt_ds = gdal.Open(vrt_txt)
    vrt_gcps = vrt_ds.GetGCPs()

    # Create PDF
    ds = gdaltest.pdf_drv.CreateCopy(out_filename, vrt_ds, options=['GEO_ENCODING=OGC_BP', 'DPI=%d' % dpi])
    ds = None

    vrt_ds = None

    # Check
    ds = gdal.Open(out_filename)
    got_gt = ds.GetGeoTransform()
    got_wkt = ds.GetProjectionRef()
    got_gcp_count = ds.GetGCPCount()
    got_gcps = ds.GetGCPs()
    got_gcp_wkt = ds.GetGCPProjection()
    got_neatline = ds.GetMetadataItem('NEATLINE')
    ds = None

    assert got_wkt == '', 'did not expect non null GetProjectionRef'

    assert got_gcp_wkt != '', 'did not expect null GetGCPProjection'

    expected_gt = [0, 1, 0, 0, 0, 1]
    for i in range(6):
        assert got_gt[i] == pytest.approx(expected_gt[i], abs=1e-8), 'did not get expected gt'

    assert got_gcp_count == len(gcp), 'did not get expected GCP count'

    for i in range(got_gcp_count):
        assert (got_gcps[i].GCPX == pytest.approx(vrt_gcps[i].GCPX, abs=1e-5) and \
           got_gcps[i].GCPY == pytest.approx(vrt_gcps[i].GCPY, abs=1e-5) and \
           got_gcps[i].GCPPixel == pytest.approx(vrt_gcps[i].GCPPixel, abs=1e-5) and \
           got_gcps[i].GCPLine == pytest.approx(vrt_gcps[i].GCPLine, abs=1e-5)), \
            ('did not get expected GCP (%d)' % i)

    got_geom = ogr.CreateGeometryFromWkt(got_neatline)
    # Not sure this is really what we want, but without any geotransform, we cannot
    # find projected coordinates
    expected_geom = ogr.CreateGeometryFromWkt('POLYGON ((2 8,2 10,2 18,16 18,16 8,2 8))')

    if ogrtest.check_feature_geometry(got_geom, expected_geom) != 0:
        print('got : %s' % got_neatline)
        print('expected : %s' % expected_geom.ExportToWkt())
        pytest.fail('bad neatline')

    gdaltest.pdf_drv.Delete(out_filename)


###############################################################################
# Check NEATLINE support

def _pdf_set_neatline(pdf_backend, geo_encoding, dpi=300):
    out_filename = 'tmp/pdf_set_neatline.pdf'

    if geo_encoding == 'ISO32000':
        neatline = 'POLYGON ((441720 3751320,441720 3750120,441920 3750120,441920 3751320,441720 3751320))'
    else:  # For OGC_BP, we can use more than 4 points
        neatline = 'POLYGON ((441720 3751320,441720 3751000,441720 3750120,441920 3750120,441920 3751320,441720 3751320))'

    # Test CreateCopy() with NEATLINE
    src_ds = gdal.Open('data/byte.tif')
    expected_gt = src_ds.GetGeoTransform()
    ds = gdaltest.pdf_drv.CreateCopy(out_filename, src_ds, options=['NEATLINE=%s' % neatline, 'GEO_ENCODING=%s' % geo_encoding, 'DPI=%d' % dpi])
    ds = None
    src_ds = None

    # Check
    ds = gdal.Open(out_filename)
    got_gt = ds.GetGeoTransform()
    got_neatline = ds.GetMetadataItem('NEATLINE')

    if pdf_is_pdfium():
        if geo_encoding == 'ISO32000':
            expected_gt = (440722.21886505646, 59.894520395349709, 0.020450745157516229, 3751318.6133243339, 0.077268565258743135, -60.009312694035692)
        max_error = 1
    else:
        max_error = 0.0001
    ds = None

    for i in range(6):
        assert got_gt[i] == pytest.approx(expected_gt[i], abs=2e-7), 'did not get expected gt'

    got_geom = ogr.CreateGeometryFromWkt(got_neatline)
    expected_geom = ogr.CreateGeometryFromWkt(neatline)

    if ogrtest.check_feature_geometry(got_geom, expected_geom, max_error=max_error) != 0:
        print('got : %s' % got_neatline)
        print('expected : %s' % expected_geom.ExportToWkt())
        pytest.fail('bad neatline')

    # Test SetMetadataItem()
    ds = gdal.Open(out_filename, gdal.GA_Update)
    neatline = 'POLYGON ((440720 3751320,440720 3750120,441920 3750120,441920 3751320,440720 3751320))'
    ds.SetMetadataItem('NEATLINE', neatline)
    ds = None

    # Check
    gdal.SetConfigOption('GDAL_PDF_GEO_ENCODING', geo_encoding)
    ds = gdal.Open(out_filename)
    got_gt = ds.GetGeoTransform()
    got_neatline = ds.GetMetadataItem('NEATLINE')

    if pdf_is_pdfium():
        if geo_encoding == 'ISO32000':
            expected_gt = (440722.36151923181, 59.93217744208814, 0.0, 3751318.7819266757, 0.0, -59.941906300000845)

    ds = None
    gdal.SetConfigOption('GDAL_PDF_GEO_ENCODING', None)

    for i in range(6):
        assert (not (expected_gt[i] == 0 and got_gt[i] != pytest.approx(expected_gt[i], abs=1e-7)) or \
           (expected_gt[i] != 0 and abs((got_gt[i] - expected_gt[i]) / expected_gt[i]) > 1e-7)), \
            'did not get expected gt'

    got_geom = ogr.CreateGeometryFromWkt(got_neatline)
    expected_geom = ogr.CreateGeometryFromWkt(neatline)

    if ogrtest.check_feature_geometry(got_geom, expected_geom, max_error=max_error) != 0:
        print('got : %s' % got_neatline)
        print('expected : %s' % expected_geom.ExportToWkt())
        pytest.fail('bad neatline')

    gdaltest.pdf_drv.Delete(out_filename)


def test_pdf_set_neatline_iso32000(poppler_or_pdfium):
    return _pdf_set_neatline(poppler_or_pdfium, 'ISO32000')


def test_pdf_set_neatline_ogc_bp(poppler_or_pdfium):
    return _pdf_set_neatline(poppler_or_pdfium, 'OGC_BP')

###############################################################################
# Check that we can generate identical file


def test_pdf_check_identity_iso32000(poppler_or_pdfium):
    out_filename = 'tmp/pdf_check_identity_iso32000.pdf'

    src_ds = gdal.Open('data/test_pdf.vrt')
    out_ds = gdaltest.pdf_drv.CreateCopy(out_filename, src_ds, options=['STREAM_COMPRESS=NONE'])
    del out_ds
    src_ds = None

    f = open('data/test_iso32000.pdf', 'rb')
    data_ref = f.read()
    f.close()

    f = open(out_filename, 'rb')
    data_got = f.read()
    f.close()

    assert data_ref == data_got, 'content does not match reference content'

    gdaltest.pdf_drv.Delete(out_filename)

###############################################################################
# Check that we can generate identical file


def test_pdf_check_identity_ogc_bp(poppler_or_pdfium):
    out_filename = 'tmp/pdf_check_identity_ogc_bp.pdf'

    src_ds = gdal.Open('data/test_pdf.vrt')
    gdal.SetConfigOption('GDAL_PDF_OGC_BP_WRITE_WKT', 'NO')
    out_ds = gdaltest.pdf_drv.CreateCopy(out_filename, src_ds, options=['GEO_ENCODING=OGC_BP', 'STREAM_COMPRESS=NONE'])
    del out_ds
    gdal.SetConfigOption('GDAL_PDF_OGC_BP_WRITE_WKT', None)
    src_ds = None

    f = open('data/test_ogc_bp.pdf', 'rb')
    data_ref = f.read()
    f.close()

    f = open(out_filename, 'rb')
    data_got = f.read()
    f.close()

    assert data_ref == data_got, 'content does not match reference content'

    gdaltest.pdf_drv.Delete(out_filename)

###############################################################################
# Check layers support


def test_pdf_layers(poppler_or_pdfium):
    if not pdf_is_poppler() and not pdf_is_pdfium():
        pytest.skip()

    ds = gdal.Open('data/adobe_style_geospatial.pdf')
    layers = ds.GetMetadata_List('LAYERS')
    cs1 = ds.GetRasterBand(1).Checksum()
    ds = None

    # if layers != ['LAYER_00_INIT_STATE=ON', 'LAYER_00_NAME=New_Data_Frame', 'LAYER_01_INIT_STATE=ON', 'LAYER_01_NAME=New_Data_Frame.Graticule', 'LAYER_02_INIT_STATE=ON', 'LAYER_02_NAME=Layers', 'LAYER_03_INIT_STATE=ON', 'LAYER_03_NAME=Layers.Measured_Grid', 'LAYER_04_INIT_STATE=ON', 'LAYER_04_NAME=Layers.Graticule']:
    assert layers == ['LAYER_00_NAME=New_Data_Frame', 'LAYER_01_NAME=New_Data_Frame.Graticule', 'LAYER_02_NAME=Layers', 'LAYER_03_NAME=Layers.Measured_Grid', 'LAYER_04_NAME=Layers.Graticule'], \
        'did not get expected layers'

    if not pdf_checksum_available():
        pytest.skip()

    # Turn a layer off
    gdal.SetConfigOption('GDAL_PDF_LAYERS_OFF', 'New_Data_Frame')
    ds = gdal.Open('data/adobe_style_geospatial.pdf')
    cs2 = ds.GetRasterBand(1).Checksum()
    ds = None
    gdal.SetConfigOption('GDAL_PDF_LAYERS_OFF', None)

    assert cs2 != cs1, 'did not get expected checksum'

    # Turn the other layer on
    gdal.SetConfigOption('GDAL_PDF_LAYERS', 'Layers')
    ds = gdal.Open('data/adobe_style_geospatial.pdf')
    cs3 = ds.GetRasterBand(1).Checksum()
    ds = None
    gdal.SetConfigOption('GDAL_PDF_LAYERS', None)

    # So the end result must be identical
    assert cs3 == cs2, 'did not get expected checksum'

    # Turn another sublayer on
    ds = gdal.OpenEx('data/adobe_style_geospatial.pdf', open_options=['LAYERS=Layers.Measured_Grid'])
    cs4 = ds.GetRasterBand(1).Checksum()
    ds = None

    assert not (cs4 == cs1 or cs4 == cs2), 'did not get expected checksum'

###############################################################################
# Test MARGIN, EXTRA_STREAM, EXTRA_LAYER_NAME and EXTRA_IMAGES options


def test_pdf_custom_layout(poppler_or_pdfium):
    js = """button = app.alert({cMsg: 'This file was generated by GDAL. Do you want to visit its website ?', cTitle: 'Question', nIcon:2, nType:2});
if (button == 4) app.launchURL('http://gdal.org/');"""

    options = ['LEFT_MARGIN=1',
               'TOP_MARGIN=2',
               'RIGHT_MARGIN=3',
               'BOTTOM_MARGIN=4',
               'DPI=300',
               'LAYER_NAME=byte_tif',
               'EXTRA_STREAM=BT 255 0 0 rg /FTimesRoman 1 Tf 1 0 0 1 1 1 Tm (Footpage string) Tj ET',
               'EXTRA_LAYER_NAME=Footpage_and_logo',
               'EXTRA_IMAGES=data/byte.tif,0.5,0.5,0.2,link=http://gdal.org/,data/byte.tif,0.5,1.5,0.2',
               'JAVASCRIPT=%s' % js]

    src_ds = gdal.Open('data/byte.tif')
    ds = gdaltest.pdf_drv.CreateCopy('tmp/pdf_custom_layout.pdf', src_ds, options=options)
    ds = None
    src_ds = None

    if pdf_is_poppler() or pdf_is_pdfium():
        ds = gdal.Open('tmp/pdf_custom_layout.pdf')
        ds.GetRasterBand(1).Checksum()
        layers = ds.GetMetadata_List('LAYERS')
        ds = None

    gdal.GetDriverByName('PDF').Delete('tmp/pdf_custom_layout.pdf')

    if pdf_is_poppler() or pdf_is_pdfium():
        assert layers == ['LAYER_00_NAME=byte_tif', 'LAYER_01_NAME=Footpage_and_logo'], \
            'did not get expected layers'

    
###############################################################################
# Test CLIPPING_EXTENT, EXTRA_RASTERS, EXTRA_RASTERS_LAYER_NAME, OFF_LAYERS, EXCLUSIVE_LAYERS options


def test_pdf_extra_rasters(poppler_or_pdfium):
    subbyte = """<VRTDataset rasterXSize="10" rasterYSize="10">
  <SRS>PROJCS["NAD27 / UTM zone 11N",GEOGCS["NAD27",DATUM["North_American_Datum_1927",SPHEROID["Clarke 1866",6378206.4,294.9786982139006,AUTHORITY["EPSG","7008"]],AUTHORITY["EPSG","6267"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],AUTHORITY["EPSG","4267"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-117],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","26711"]]</SRS>
  <GeoTransform>  4.4102000000000000e+05,  6.0000000000000000e+01,  0.0000000000000000e+00,  3.7510200000000000e+06,  0.0000000000000000e+00, -6.0000000000000000e+01</GeoTransform>
  <Metadata>
    <MDI key="AREA_OR_POINT">Area</MDI>
  </Metadata>
  <Metadata domain="IMAGE_STRUCTURE">
    <MDI key="INTERLEAVE">BAND</MDI>
  </Metadata>
  <VRTRasterBand dataType="Byte" band="1">
    <Metadata />
    <ColorInterp>Gray</ColorInterp>
    <SimpleSource>
      <SourceFilename relativeToVRT="1">../data/byte.tif</SourceFilename>
      <SourceBand>1</SourceBand>
      <SourceProperties RasterXSize="20" RasterYSize="20" DataType="Byte" BlockXSize="20" BlockYSize="20" />
      <SrcRect xOff="5" yOff="5" xSize="10" ySize="10" />
      <DstRect xOff="0" yOff="0" xSize="10" ySize="10" />
    </SimpleSource>
  </VRTRasterBand>
</VRTDataset>"""

    f = open('tmp/subbyte.vrt', 'wt')
    f.write(subbyte)
    f.close()

    options = ['MARGIN=1',
               'DPI=300',
               'WRITE_USERUNIT=YES',
               'CLIPPING_EXTENT=440780,3750180,441860,3751260',
               'LAYER_NAME=byte_tif',
               'EXTRA_RASTERS=tmp/subbyte.vrt',
               'EXTRA_RASTERS_LAYER_NAME=subbyte',
               'OFF_LAYERS=byte_tif',
               'EXCLUSIVE_LAYERS=byte_tif,subbyte']

    src_ds = gdal.Open('data/byte.tif')
    ds = gdaltest.pdf_drv.CreateCopy('tmp/pdf_extra_rasters.pdf', src_ds, options=options)
    ds = None
    src_ds = None

    if pdf_is_poppler() or pdf_is_pdfium():
        ds = gdal.Open('tmp/pdf_extra_rasters.pdf')
        cs = ds.GetRasterBand(1).Checksum()
        layers = ds.GetMetadata_List('LAYERS')
        ds = None

    gdal.GetDriverByName('PDF').Delete('tmp/pdf_extra_rasters.pdf')
    os.unlink('tmp/subbyte.vrt')

    if pdf_is_poppler() or pdf_is_pdfium():
        assert layers == ['LAYER_00_NAME=byte_tif', 'LAYER_01_NAME=subbyte'], \
            'did not get expected layers'
    assert not (pdf_is_poppler() and (cs != 7926 and cs != 8177 and cs != 8174 and cs != 8165)), \
        'bad checksum'

###############################################################################
# Test adding a OGR datasource


def test_pdf_write_ogr(poppler_or_pdfium):
    f = gdal.VSIFOpenL('tmp/test.csv', 'wb')
    data = """id,foo,WKT,style
1,bar,"MULTIPOLYGON (((440720 3751320,440720 3750120,441020 3750120,441020 3751320,440720 3751320),(440800 3751200,440900 3751200,440900 3751000,440800 3751000,440800 3751200)),((441720 3751320,441720 3750120,441920 3750120,441920 3751320,441720 3751320)))",
2,baz,"LINESTRING(440720 3751320,441920 3750120)","PEN(c:#FF0000,w:5pt,p:""2px 1pt"")"
3,baz2,"POINT(441322.400 3750717.600)","PEN(c:#FF00FF,w:10px);BRUSH(fc:#FFFFFF);LABEL(c:#FF000080, f:""Arial, Helvetica"", a:45, s:12pt, t:""Hello World!"")"
4,baz3,"POINT(0 0)",""
"""
    gdal.VSIFWriteL(data, 1, len(data), f)
    gdal.VSIFCloseL(f)

    f = gdal.VSIFOpenL('tmp/test.vrt', 'wb')
    data = """<OGRVRTDataSource>
  <OGRVRTLayer name="test">
    <SrcDataSource relativeToVRT="0" shared="1">tmp/test.csv</SrcDataSource>
    <SrcLayer>test</SrcLayer>
    <GeometryType>wkbUnknown</GeometryType>
    <LayerSRS>EPSG:26711</LayerSRS>
    <Field name="id" type="Integer" src="id"/>
    <Field name="foo" type="String" src="foo"/>
    <Field name="WKT" type="String" src="WKT"/>
    <Style>style</Style>
  </OGRVRTLayer>
</OGRVRTDataSource>
"""
    gdal.VSIFWriteL(data, 1, len(data), f)
    gdal.VSIFCloseL(f)

    options = ['OGR_DATASOURCE=tmp/test.vrt', 'OGR_DISPLAY_LAYER_NAMES=A_Layer', 'OGR_DISPLAY_FIELD=foo']

    src_ds = gdal.Open('data/byte.tif')
    ds = gdaltest.pdf_drv.CreateCopy('tmp/pdf_write_ogr.pdf', src_ds, options=options)
    ds = None
    src_ds = None

    if pdf_is_poppler() or pdf_is_pdfium():
        ds = gdal.Open('tmp/pdf_write_ogr.pdf')
        cs_ref = ds.GetRasterBand(1).Checksum()
        layers = ds.GetMetadata_List('LAYERS')
        ds = None

    ogr_ds = ogr.Open('tmp/pdf_write_ogr.pdf')
    ogr_lyr = ogr_ds.GetLayer(0)
    feature_count = ogr_lyr.GetFeatureCount()
    ogr_ds = None

    if pdf_is_poppler() or pdf_is_pdfium():

        cs_tab = []
        rendering_options = ['RASTER', 'VECTOR', 'TEXT', 'RASTER,VECTOR', 'RASTER,TEXT', 'VECTOR,TEXT', 'RASTER,VECTOR,TEXT']
        for opt in rendering_options:
            gdal.ErrorReset()
            ds = gdal.OpenEx('tmp/pdf_write_ogr.pdf', open_options=['RENDERING_OPTIONS=%s' % opt])
            cs = ds.GetRasterBand(1).Checksum()
            # When misconfigured Poppler with fonts, use this to avoid error
            if 'TEXT' in opt and gdal.GetLastErrorMsg().find('font') >= 0:
                cs = -cs
            cs_tab.append(cs)
            ds = None

        # Test that all combinations give a different result
        for i, roi in enumerate(rendering_options):
            # print('Checksum %s: %d' % (rendering_options[i], cs_tab[i]) )
            for j in range(i + 1, len(rendering_options)):
                if cs_tab[i] == cs_tab[j] and cs_tab[i] >= 0 and cs_tab[j] >= 0:
                    print('Checksum %s: %d' % (roi, cs_tab[i]))
                    pytest.fail('Checksum %s: %d' % (rendering_options[j], cs_tab[j]))

        # And test that RASTER,VECTOR,TEXT is the default rendering
        assert abs(cs_tab[len(rendering_options) - 1]) == cs_ref

    gdal.GetDriverByName('PDF').Delete('tmp/pdf_write_ogr.pdf')

    gdal.Unlink('tmp/test.csv')
    gdal.Unlink('tmp/test.vrt')

    if pdf_is_poppler() or pdf_is_pdfium():
        assert layers == ['LAYER_00_NAME=A_Layer', 'LAYER_01_NAME=A_Layer.Text'], \
            'did not get expected layers'

    # Should have filtered out id = 4
    assert feature_count == 3, 'did not get expected feature count'

###############################################################################
# Test adding a OGR datasource with reprojection of OGR SRS to GDAL SRS


def test_pdf_write_ogr_with_reprojection(poppler_or_pdfium):

    f = gdal.VSIFOpenL('tmp/test.csv', 'wb')
    data = """WKT,id
"POINT (-117.641059792392142 33.902263065734573)",1
"POINT (-117.64098016484607 33.891620919037436)",2
"POINT (-117.62829768175105 33.902328822481238)",3
"POINT (-117.628219639160108 33.891686649558416)",4
"POINT (-117.634639319537328 33.896975031776485)",5
"POINT (-121.488694798047447 0.0)",6
"""

    gdal.VSIFWriteL(data, 1, len(data), f)
    gdal.VSIFCloseL(f)

    f = gdal.VSIFOpenL('tmp/test.vrt', 'wb')
    data = """<OGRVRTDataSource>
  <OGRVRTLayer name="test">
    <SrcDataSource relativeToVRT="0" shared="1">tmp/test.csv</SrcDataSource>
    <SrcLayer>test</SrcLayer>
    <GeometryType>wkbUnknown</GeometryType>
    <LayerSRS>+proj=longlat +datum=NAD27</LayerSRS>
    <Field name="id" type="Integer" src="id"/>
  </OGRVRTLayer>
</OGRVRTDataSource>
"""
    gdal.VSIFWriteL(data, 1, len(data), f)
    gdal.VSIFCloseL(f)

    options = ['OGR_DATASOURCE=tmp/test.vrt', 'OGR_DISPLAY_LAYER_NAMES=A_Layer', 'OGR_DISPLAY_FIELD=foo']

    src_ds = gdal.Open('data/byte.tif')
    ds = gdaltest.pdf_drv.CreateCopy('tmp/pdf_write_ogr_with_reprojection.pdf', src_ds, options=options)
    del ds
    src_ds = None

    ogr_ds = ogr.Open('tmp/pdf_write_ogr_with_reprojection.pdf')
    ogr_lyr = ogr_ds.GetLayer(0)
    feature_count = ogr_lyr.GetFeatureCount()
    ogr_ds = None

    gdal.GetDriverByName('PDF').Delete('tmp/pdf_write_ogr_with_reprojection.pdf')

    gdal.Unlink('tmp/test.csv')
    gdal.Unlink('tmp/test.vrt')

    # Should have filtered out id = 6
    assert feature_count == 5, 'did not get expected feature count'

###############################################################################
# Test direct copy of source JPEG file


def test_pdf_jpeg_direct_copy(poppler_or_pdfium):
    if gdal.GetDriverByName('JPEG') is None:
        pytest.skip()

    src_ds = gdal.Open('data/byte_with_xmp.jpg')
    ds = gdaltest.pdf_drv.CreateCopy('tmp/pdf_jpeg_direct_copy.pdf', src_ds, options=['XMP=NO'])
    ds = None
    src_ds = None

    ds = gdal.Open('tmp/pdf_jpeg_direct_copy.pdf')
    # No XMP at PDF level
    assert ds.GetMetadata('xml:XMP') is None
    assert ds.RasterXSize == 20
    assert not (pdf_checksum_available() and ds.GetRasterBand(1).Checksum() == 0)
    ds = None

    # But we can find the original XMP from the JPEG file !
    f = open('tmp/pdf_jpeg_direct_copy.pdf', 'rb')
    data = f.read().decode('ISO-8859-1')
    f.close()
    offset = data.find('ns.adobe.com')

    gdal.Unlink('tmp/pdf_jpeg_direct_copy.pdf')

    assert offset != -1

###############################################################################
# Test direct copy of source JPEG file within VRT file


def test_pdf_jpeg_in_vrt_direct_copy(poppler_or_pdfium):
    if gdal.GetDriverByName('JPEG') is None:
        pytest.skip()

    src_ds = gdal.Open("""<VRTDataset rasterXSize="20" rasterYSize="20">
  <SRS>PROJCS["NAD27 / UTM zone 11N",GEOGCS["NAD27",DATUM["North_American_Datum_1927",SPHEROID["Clarke 1866",6378206.4,294.9786982139006,AUTHORITY["EPSG","7008"]],AUTHORITY["EPSG","6267"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],AUTHORITY["EPSG","4267"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-117],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","26711"]]</SRS>
  <GeoTransform>  4.4072000000000000e+05,  6.0000000000000000e+01,  0.0000000000000000e+00,  3.7513200000000000e+06,  0.0000000000000000e+00, -6.0000000000000000e+01</GeoTransform>
  <VRTRasterBand dataType="Byte" band="1">
    <SimpleSource>
      <SourceFilename relativeToVRT="0">data/byte_with_xmp.jpg</SourceFilename>
      <SourceBand>1</SourceBand>
      <SourceProperties RasterXSize="20" RasterYSize="20" DataType="Byte" BlockXSize="20" BlockYSize="1" />
      <SrcRect xOff="0" yOff="0" xSize="20" ySize="20" />
      <DstRect xOff="0" yOff="0" xSize="20" ySize="20" />
    </SimpleSource>
  </VRTRasterBand>
</VRTDataset>""")
    ds = gdaltest.pdf_drv.CreateCopy('tmp/pdf_jpeg_in_vrt_direct_copy.pdf', src_ds)
    ds = None
    src_ds = None

    ds = gdal.Open('tmp/pdf_jpeg_in_vrt_direct_copy.pdf')
    # No XMP at PDF level
    assert ds.GetMetadata('xml:XMP') is None
    assert ds.RasterXSize == 20
    assert not (pdf_checksum_available() and ds.GetRasterBand(1).Checksum() == 0)
    ds = None

    # But we can find the original XMP from the JPEG file !
    f = open('tmp/pdf_jpeg_in_vrt_direct_copy.pdf', 'rb')
    data = f.read().decode('ISO-8859-1')
    f.close()
    offset = data.find('ns.adobe.com')

    gdal.Unlink('tmp/pdf_jpeg_in_vrt_direct_copy.pdf')

    assert offset != -1

###############################################################################
# Test reading georeferencing attached to an image, and not to the page (#4695)


@pytest.mark.parametrize(
    'src_filename',
    ['data/byte.tif', 'data/rgbsmall.tif']
)
def pdf_georef_on_image(src_filename, pdf_backend):
    src_ds = gdal.Open(src_filename)
    gdal.SetConfigOption('GDAL_PDF_WRITE_GEOREF_ON_IMAGE', 'YES')
    out_ds = gdaltest.pdf_drv.CreateCopy('tmp/pdf_georef_on_image.pdf', src_ds, options=['MARGIN=10', 'GEO_ENCODING=NONE'])
    del out_ds
    gdal.SetConfigOption('GDAL_PDF_WRITE_GEOREF_ON_IMAGE', None)
    if pdf_checksum_available():
        src_cs = src_ds.GetRasterBand(1).Checksum()
    else:
        src_cs = 0
    src_ds = None

    ds = gdal.Open('tmp/pdf_georef_on_image.pdf')
    subdataset_name = ds.GetMetadataItem('SUBDATASET_1_NAME', 'SUBDATASETS')
    ds = None

    ds = gdal.Open(subdataset_name)
    got_wkt = ds.GetProjectionRef()
    if pdf_checksum_available():
        got_cs = ds.GetRasterBand(1).Checksum()
    else:
        got_cs = 0
    ds = None

    gdal.GetDriverByName('PDF').Delete('tmp/pdf_georef_on_image.pdf')

    assert got_wkt != '', 'did not get projection'

    assert not pdf_checksum_available() or src_cs == got_cs, 'did not get same checksum'


###############################################################################
# Test writing a PDF that hits Acrobat limits in term of page dimensions (#5412)


def test_pdf_write_huge(poppler_or_pdfium):
    if pdf_is_poppler() or pdf_is_pdfium():
        tmp_filename = '/vsimem/pdf_write_huge.pdf'
    else:
        tmp_filename = 'tmp/pdf_write_huge.pdf'

    for (xsize, ysize) in [(19200, 1), (1, 19200)]:
        src_ds = gdal.GetDriverByName('MEM').Create('', xsize, ysize, 1)
        ds = gdaltest.pdf_drv.CreateCopy(tmp_filename, src_ds)
        ds = None
        ds = gdal.Open(tmp_filename)
        assert int(ds.GetMetadataItem('DPI')) == 96
        assert (ds.RasterXSize == src_ds.RasterXSize and \
                ds.RasterYSize == src_ds.RasterYSize)
        ds = None

        gdal.ErrorReset()
        gdal.PushErrorHandler('CPLQuietErrorHandler')
        ds = gdaltest.pdf_drv.CreateCopy(tmp_filename, src_ds, options=['DPI=72'])
        gdal.PopErrorHandler()
        msg = gdal.GetLastErrorMsg()
        assert msg != ''
        ds = None
        ds = gdal.Open(tmp_filename)
        assert int(ds.GetMetadataItem('DPI')) == 72
        ds = None

        src_ds = None

    for option in ['LEFT_MARGIN=14400', 'TOP_MARGIN=14400']:
        src_ds = gdal.GetDriverByName('MEM').Create('', 1, 1, 1)
        gdal.ErrorReset()
        gdal.PushErrorHandler('CPLQuietErrorHandler')
        ds = gdaltest.pdf_drv.CreateCopy(tmp_filename, src_ds, options=[option])
        gdal.PopErrorHandler()
        msg = gdal.GetLastErrorMsg()
        assert msg != ''
        ds = None
        ds = gdal.Open(tmp_filename)
        assert int(ds.GetMetadataItem('DPI')) == 72
        ds = None

        src_ds = None

    gdal.Unlink(tmp_filename)

###############################################################################
# Test creating overviews


def test_pdf_overviews(poppler_or_pdfium):
    if not pdf_is_poppler() and not pdf_is_pdfium():
        pytest.skip()
    tmp_filename = '/vsimem/pdf_overviews.pdf'

    src_ds = gdal.GetDriverByName('MEM').Create('', 1024, 1024, 3)
    for i in range(3):
        src_ds.GetRasterBand(i + 1).Fill(255)
    ds = gdaltest.pdf_drv.CreateCopy(tmp_filename, src_ds)
    src_ds = None
    ds = None
    ds = gdal.Open(tmp_filename)
    before = ds.GetRasterBand(1).GetOverviewCount()
    ds.GetRasterBand(1).GetOverview(-1)
    ds.GetRasterBand(1).GetOverview(10)
    if before >= 1:
        assert pdf_is_pdfium(), 'No overview expected at this point!'
        cs = ds.GetRasterBand(1).GetOverview(0).Checksum()
        assert cs == 5934
    elif pdf_is_pdfium():
        pytest.fail('Overview expected at this point!')
    ds.BuildOverviews('NONE', [2])
    after = ds.GetRasterBand(1).GetOverviewCount()
    assert after == 1
    cs = ds.GetRasterBand(1).GetOverview(0).Checksum()
    assert cs == 0
    ds = None

    gdaltest.pdf_drv.Delete(tmp_filename)

###############################################################################
# Test password


def test_pdf_password(poppler_or_pdfium_or_podofo):
    # User password of this test file is user_password and owner password is
    # owner_password

    # No password
    with gdaltest.error_handler():
        ds = gdal.Open('data/byte_enc.pdf')
    assert ds is None

    # Wrong password
    with gdaltest.error_handler():
        ds = gdal.OpenEx('data/byte_enc.pdf', open_options=['USER_PWD=wrong_password'])
    assert ds is None

    # Correct password
    ds = gdal.OpenEx('data/byte_enc.pdf', open_options=['USER_PWD=user_password'])
    assert ds is not None

    import test_cli_utilities
    if test_cli_utilities.get_gdal_translate_path() is None:
        pytest.skip()

    # Test ASK_INTERACTIVE with wrong password
    cmd_line = test_cli_utilities.get_gdal_translate_path() + ' data/byte_enc.pdf /vsimem/out.tif -q -oo USER_PWD=ASK_INTERACTIVE < tmp/password.txt'
    if sys.platform != 'win32':
        cmd_line += ' >/dev/null 2>/dev/null'

    open('tmp/password.txt', 'wb').write('wrong_password'.encode('ASCII'))
    ret = os.system(cmd_line)
    os.unlink('tmp/password.txt')
    assert ret != 0

    # Test ASK_INTERACTIVE with correct password
    open('tmp/password.txt', 'wb').write('user_password'.encode('ASCII'))
    ret = os.system(cmd_line)
    os.unlink('tmp/password.txt')
    assert ret == 0

###############################################################################
# Test multi page support


def test_pdf_multipage(poppler_or_pdfium_or_podofo):
    # byte_and_rgbsmall_2pages.pdf was generated with :
    # 1) gdal_translate gcore/data/byte.tif byte.pdf -of PDF
    # 2) gdal_translate gcore/data/rgbsmall.tif rgbsmall.pdf -of PDF
    # 3)  ~/install-podofo-0.9.3/bin/podofomerge byte.pdf rgbsmall.pdf byte_and_rgbsmall_2pages.pdf

    ds = gdal.Open('data/byte_and_rgbsmall_2pages.pdf')
    subdatasets = ds.GetSubDatasets()
    expected_subdatasets = [('PDF:1:data/byte_and_rgbsmall_2pages.pdf', 'Page 1 of data/byte_and_rgbsmall_2pages.pdf'), ('PDF:2:data/byte_and_rgbsmall_2pages.pdf', 'Page 2 of data/byte_and_rgbsmall_2pages.pdf')]
    assert subdatasets == expected_subdatasets, 'did not get expected subdatasets'
    ds = None

    ds = gdal.Open('PDF:1:data/byte_and_rgbsmall_2pages.pdf')
    assert ds.RasterXSize == 20, 'wrong width'

    ds2 = gdal.Open('PDF:2:data/byte_and_rgbsmall_2pages.pdf')
    assert ds2.RasterXSize == 50, 'wrong width'

    with gdaltest.error_handler():
        ds3 = gdal.Open('PDF:0:data/byte_and_rgbsmall_2pages.pdf')
    assert ds3 is None

    with gdaltest.error_handler():
        ds3 = gdal.Open('PDF:3:data/byte_and_rgbsmall_2pages.pdf')
    assert ds3 is None

    with gdaltest.error_handler():
        ds = gdal.Open('PDF:1:/does/not/exist.pdf')
    assert ds is None

###############################################################################
# Test PAM metadata support


def test_pdf_metadata(poppler_or_pdfium):
    gdal.Translate('tmp/pdf_metadata.pdf', 'data/byte.tif', format='PDF', metadataOptions=['FOO=BAR'])
    ds = gdal.Open('tmp/pdf_metadata.pdf')
    md = ds.GetMetadata()
    assert 'FOO' in md
    ds = None
    ds = gdal.Open('tmp/pdf_metadata.pdf')
    assert ds.GetMetadataItem('FOO') == 'BAR'
    ds = None

    gdal.GetDriverByName('PDF').Delete('tmp/pdf_metadata.pdf')

###############################################################################
# Test PAM georef support


def test_pdf_pam_georef(poppler_or_pdfium):
    src_ds = gdal.Open('data/byte.tif')

    # Default behaviour should result in no PAM file
    gdaltest.pdf_drv.CreateCopy('tmp/pdf_pam_georef.pdf', src_ds)
    assert not os.path.exists('tmp/pdf_pam_georef.pdf.aux.xml')

    # Now disable internal georeferencing, so georef should go to PAM
    gdaltest.pdf_drv.CreateCopy('tmp/pdf_pam_georef.pdf', src_ds, options=['GEO_ENCODING=NONE'])
    assert os.path.exists('tmp/pdf_pam_georef.pdf.aux.xml')

    ds = gdal.Open('tmp/pdf_pam_georef.pdf')
    assert ds.GetGeoTransform() == src_ds.GetGeoTransform()
    assert ds.GetProjectionRef() == src_ds.GetProjectionRef()
    ds = None

    gdal.GetDriverByName('PDF').Delete('tmp/pdf_pam_georef.pdf')


###############################################################################
# Test XML composition


def test_pdf_composition():

    xml_content = """<PDFComposition>
    <Metadata>
        <Author>Even</Author>
        <XMP>&lt;?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?&gt;
&lt;x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='Image::ExifTool 7.89'&gt;
&lt;rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'&gt;
&lt;/rdf:RDF&gt;
&lt;/x:xmpmeta&gt;
&lt;?xpacket end='w'?&gt;
</XMP>
    </Metadata>
    <Javascript>button = app.alert({cMsg: 'This file was generated by GDAL. Do you want to visit its website ?', cTitle: 'Question', nIcon:2, nType:2});
if (button == 4) app.launchURL('http://gdal.org/');</Javascript>

    <LayerTree>
        <Layer id="l1" name="Satellite imagery"/>
        <Layer id="l2" name="OSM data">
            <Layer id="l2.1" name="Roads" initiallyVisible="false"/>
            <Layer id="l2.2" name="Buildings" mutuallyExclusiveGroupId="group1">
                <Layer id="l2.2.text" name="Buildings name"/>
            </Layer>
            <Layer id="l2.3" name="Cadastral parcels" mutuallyExclusiveGroupId="group1"/>
        </Layer>
    </LayerTree>

    <Page>
        <DPI>72</DPI>
        <Width>20</Width>
        <Height>10</Height>

        <Georeferencing ISO32000ExtensionFormat="true" OGCBestPracticeFormat="true"> 
            <SRS dataAxisToSRSAxisMapping="2,1">EPSG:4326</SRS> 
            <BoundingBox x1="1" y1="1" x2="19" y2="9"/>
            <BoundingPolygon>POLYGON((1 1,19 1,19 9,1 9,1 1))</BoundingPolygon>
            <ControlPoint x="1"  y="1"  GeoY="-90"  GeoX="-180"/>
            <ControlPoint x="19" y="1"  GeoY="90"   GeoX="180"/>
            <ControlPoint x="1"  y="9"  GeoY="-90"  GeoX="-180"/>
            <ControlPoint x="19" y="9"  GeoY="90"   GeoX="180"/>
        </Georeferencing>

        <Content streamCompression="NONE">
            <IfLayerOn layerId="l1">
                <Raster dataset="data/byte.tif" x1="1" y1="1" x2="9" y2="9">
                    <Blending function="Multiply" opacity="0.7"/>
                </Raster>
            </IfLayerOn>
            <Vector dataset="/vsimem/test.csv" layer="test" visible="false">
                <LogicalStructure>
                    <ExcludeAllFields>true</ExcludeAllFields>
                </LogicalStructure>
            </Vector>
            <Vector dataset="/vsimem/test2.csv" layer="test2" visible="false"
                    linkAttribute="link">
                <LogicalStructure displayLayerName="another layer" fieldToDisplay="id">
                    <IncludeField>id</IncludeField>
                    <IncludeField>link</IncludeField>
                </LogicalStructure>
            </Vector>
            <Vector dataset="/vsimem/sym.csv" layer="sym">
                <LogicalStructure>
                    <ExcludeField>WKT</ExcludeField>
                    <ExcludeField>OGR_STYLE</ExcludeField>
                </LogicalStructure>
            </Vector>
            <VectorLabel dataset="/vsimem/label.csv" layer="label"/>
        </Content>
    </Page>
</PDFComposition>"""

    gdal.FileFromMemBuffer("/vsimem/test.csv",
    """id,WKT
1,"POLYGON((0.5 0.5,0.5 9.5,10 9.5,10 0.5,0.5 0.5))"
2,"POLYGON((10.5 0.5,10.5 4.5,20 4.5,20 0.5,10.5 0.5))"
""")

    gdal.FileFromMemBuffer("/vsimem/test2.csv",
    """id,link,WKT
3,"http://gdal.org","POLYGON((10.5 5.5,10.5 9.5,20 9.5,20 5.5,10.5 5.5))"
""")

    gdal.FileFromMemBuffer("/vsimem/sym.csv",
"""id,WKT,OGR_STYLE
1,"POINT(15 7)","SYMBOL(c:#00FF00,id:""ogr-sym-1"",s:1pt)"
2,"POINT(15 5)","SYMBOL(c:#00FF0077,id:""ogr-sym-1"",s:1pt)"
3,"POINT(15 3)","SYMBOL(id:""data/byte.tif"",s:1pt)"
""")

    gdal.FileFromMemBuffer("/vsimem/label.csv",
"""id,WKT,OGR_STYLE
1,"POINT(15 1)","LABEL(t:""foo"",s:1pt)"
""")

    out_filename = "tmp/tmp.pdf"
    out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                     options = ['COMPOSITION_FILE=' + xml_content])
    gdal.Unlink("/vsimem/test.csv")
    gdal.Unlink("/vsimem/test2.csv")
    gdal.Unlink("/vsimem/sym.csv")
    gdal.Unlink("/vsimem/label.csv")
    assert out_ds
    assert gdal.GetLastErrorMsg() == ''

    f = open('data/test_pdf_composition.pdf', 'rb')
    data_ref = f.read()
    f.close()

    f = open(out_filename, 'rb')
    data_got = f.read()
    f.close()

    assert data_ref == data_got, 'content does not match reference content'

    gdal.Unlink(out_filename)


def test_pdf_composition_raster_tiled_blending():

    xml_content = """<PDFComposition>
    <Page>
        <DPI>72</DPI>
        <Width>20</Width>
        <Height>20</Height>
        <Content streamCompression="NONE">
            <Raster dataset="data/byte.tif" tileSize="16">
                <Blending function="Multiply" opacity="0.7"/>
            </Raster>
        </Content>
    </Page>
</PDFComposition>"""

    out_filename = "tmp/tmp.pdf"
    out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                     options = ['COMPOSITION_FILE=' + xml_content])
    assert out_ds
    assert gdal.GetLastErrorMsg() == ''

    f = open('data/test_pdf_composition_raster_tiled_blending.pdf', 'rb')
    data_ref = f.read()
    f.close()

    f = open(out_filename, 'rb')
    data_got = f.read()
    f.close()

    assert data_ref == data_got, 'content does not match reference content'

    gdal.Unlink(out_filename)


def test_pdf_composition_pdf_content(poppler_or_pdfium_or_podofo):

    xml_content = """<PDFComposition>
    <Page>
        <Width>20</Width>
        <Height>20</Height>
        <Content streamCompression="NONE">
            <PDF dataset="data/test_iso32000.pdf">
                <Blending function="Normal" opacity="1"/>
            </PDF>
        </Content>
    </Page>
</PDFComposition>"""

    out_filename = "tmp/tmp.pdf"
    out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                     options = ['COMPOSITION_FILE=' + xml_content])
    assert out_ds
    assert gdal.GetLastErrorMsg() == ''

    # Poppler output
    f = open('data/test_pdf_composition_pdf_content.pdf', 'rb')
    data_ref = f.read()
    f.close()

    # PDFium output
    f = open('data/test_pdf_composition_pdf_content_pdfium.pdf', 'rb')
    data_ref_pdfium = f.read()
    f.close()

    f = open(out_filename, 'rb')
    data_got = f.read()
    f.close()

    assert data_got in (data_ref, data_ref_pdfium), 'content does not match reference content'

    gdal.Unlink(out_filename)


def test_pdf_composition_error_pdf_content_missing_filename(poppler_or_pdfium_or_podofo):

    xml_content = """<PDFComposition>
    <Page>
        <Width>20</Width>
        <Height>20</Height>
        <Content>
            <PDF/>
        </Content>
    </Page>
</PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == 'Missing dataset'
    gdal.Unlink(out_filename)


def test_pdf_composition_error_pdf_content_non_existing(poppler_or_pdfium_or_podofo):

    xml_content = """<PDFComposition>
    <Page>
        <Width>20</Width>
        <Height>20</Height>
        <Content>
            <PDF dataset="/vsimem/non_existing.pdf"/>
        </Content>
    </Page>
</PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == '/vsimem/non_existing.pdf is not a valid PDF file'
    gdal.Unlink(out_filename)


def test_pdf_composition_error_pdf_content_missing_contents(poppler_or_pdfium_or_podofo):

    xml_content = """<PDFComposition>
    <Page>
        <Width>20</Width>
        <Height>20</Height>
        <Content>
            <PDF dataset="data/missing_contents.pdf"/>
        </Content>
    </Page>
</PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == 'Missing Contents'
    gdal.Unlink(out_filename)


def test_pdf_composition_error_pdf_content_missing_contents_stream(poppler_or_pdfium_or_podofo):

    xml_content = """<PDFComposition>
    <Page>
        <Width>20</Width>
        <Height>20</Height>
        <Content>
            <PDF dataset="data/missing_stream.pdf"/>
        </Content>
    </Page>
</PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() in ('Missing Contents stream', 'data/missing_stream.pdf is not a valid PDF file')
    gdal.Unlink(out_filename)


def test_pdf_composition_error_pdf_content_missing_resources(poppler_or_pdfium_or_podofo):

    xml_content = """<PDFComposition>
    <Page>
        <Width>20</Width>
        <Height>20</Height>
        <Content>
            <PDF dataset="data/missing_resources.pdf"/>
        </Content>
    </Page>
</PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == 'Missing Resources'
    gdal.Unlink(out_filename)


def test_pdf_composition_raster_georeferenced():

    xml_content = """<PDFComposition>
    <Page>
        <DPI>72</DPI>
        <Width>110</Width>
        <Height>110</Height>

        <Georeferencing id="georeferenced"> 
            <SRS>EPSG:26711</SRS> 
            <BoundingBox x1="10" y1="5" x2="105" y2="100"/>
            <ControlPoint x="5"  y="5"   GeoY="3750120"  GeoX="440720"/>
            <ControlPoint x="5"  y="105"  GeoY="3751320"  GeoX="440720"/>
            <ControlPoint x="105"  y="5"  GeoY="3750120"  GeoX="441920"/>
            <ControlPoint x="105" y="105"  GeoY="3751320"  GeoX="441920"/>
        </Georeferencing>

        <Content streamCompression="NONE">
                <Raster georeferencingId="georeferenced" dataset="data/byte.tif"/>
        </Content>

    </Page>

</PDFComposition>
"""

    out_filename = "tmp/tmp.pdf"
    with gdaltest.config_option('PDF_COORD_DOUBLE_PRECISION', '12'):
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                        options = ['COMPOSITION_FILE=' + xml_content])
    assert out_ds
    assert gdal.GetLastErrorMsg() == ''

    f = open('data/test_pdf_composition_raster_georeferenced.pdf', 'rb')
    data_ref = f.read()
    f.close()

    f = open(out_filename, 'rb')
    data_got = f.read()
    f.close()

    assert data_ref == data_got, 'content does not match reference content'

    gdal.Unlink(out_filename)


def test_pdf_composition_vector_georeferenced():

    xml_content = """<PDFComposition>
    <Page>
        <DPI>72</DPI>
        <Width>110</Width>
        <Height>110</Height>

        <Georeferencing id="georeferenced"> 
            <SRS dataAxisToSRSAxisMapping="2,1">EPSG:4326</SRS> 
            <BoundingBox x1="10" y1="5" x2="105" y2="105"/>
            <ControlPoint x="5"  y="5"   GeoY="49"  GeoX="2"/>
            <ControlPoint x="5"  y="105"  GeoY="50"  GeoX="2"/>
            <ControlPoint x="105"  y="5"  GeoY="49"  GeoX="3"/>
            <ControlPoint x="105" y="105"  GeoY="50"  GeoX="3"/>
        </Georeferencing>

        <Content streamCompression="NONE">
                <Vector georeferencingId="georeferenced" dataset="/vsimem/test.shp" layer="test">
                    <LogicalStructure/>
                </Vector>
                <VectorLabel georeferencingId="georeferenced" dataset="/vsimem/test.shp" layer="test"/>
        </Content>

    </Page>

</PDFComposition>
"""

    ds = ogr.GetDriverByName("ESRI Shapefile").CreateDataSource('/vsimem/test.shp')
    srs = osr.SpatialReference()
    srs.ImportFromEPSG(4326)
    lyr = ds.CreateLayer('test', srs = srs)
    lyr.CreateField(ogr.FieldDefn('OGR_STYLE'))
    f = ogr.Feature(lyr.GetLayerDefn())
    f.SetGeometryDirectly(ogr.CreateGeometryFromWkt('POINT(2.5 49.5)'))
    f['OGR_STYLE'] = 'SYMBOL(c:#00FF00,id:"ogr-sym-1",s:1pt);LABEL(t:"foo",s:12pt)'
    lyr.CreateFeature(f)
    f = ogr.Feature(lyr.GetLayerDefn())
    f.SetGeometryDirectly(ogr.CreateGeometryFromWkt('POINT(0 0)'))
    lyr.CreateFeature(f)
    ds = None

    out_filename = "tmp/tmp.pdf"
    out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                     options = ['COMPOSITION_FILE=' + xml_content])
    assert out_ds
    assert gdal.GetLastErrorMsg() == ''

    ogr.GetDriverByName("ESRI Shapefile").DeleteDataSource('/vsimem/test.shp')

    f = open('data/test_pdf_composition_vector_georeferenced.pdf', 'rb')
    data_ref = f.read()
    f.close()

    f = open(out_filename, 'rb')
    data_got = f.read()
    f.close()

    assert data_ref == data_got, 'content does not match reference content'

    gdal.Unlink(out_filename)


def test_pdf_composition_vector_georeferenced_reprojected():

    xml_content = """<PDFComposition>
    <Page>
        <DPI>72</DPI>
        <Width>110</Width>
        <Height>110</Height>

        <Georeferencing id="georeferenced"> 
            <SRS dataAxisToSRSAxisMapping="2,1">EPSG:4326</SRS> 
            <BoundingBox x1="10" y1="5" x2="105" y2="105"/>
            <ControlPoint x="5"  y="5"   GeoY="49"  GeoX="2"/>
            <ControlPoint x="5"  y="105"  GeoY="50"  GeoX="2"/>
            <ControlPoint x="105"  y="5"  GeoY="49"  GeoX="3"/>
            <ControlPoint x="105" y="105"  GeoY="50"  GeoX="3"/>
        </Georeferencing>

        <Content streamCompression="NONE">
                <Vector georeferencingId="georeferenced" dataset="/vsimem/test.shp" layer="test">
                    <LogicalStructure/>
                </Vector>
                <VectorLabel georeferencingId="georeferenced" dataset="/vsimem/test.shp" layer="test"/>
        </Content>

    </Page>

</PDFComposition>
"""

    ds = ogr.GetDriverByName("ESRI Shapefile").CreateDataSource('/vsimem/test.shp')
    srs = osr.SpatialReference()
    srs.ImportFromEPSG(32631)
    lyr = ds.CreateLayer('test', srs = srs)
    lyr.CreateField(ogr.FieldDefn('OGR_STYLE'))
    f = ogr.Feature(lyr.GetLayerDefn())
    f.SetGeometryDirectly(ogr.CreateGeometryFromWkt('POINT(463796.280705071 5483160.94881072)'))
    f['OGR_STYLE'] = 'SYMBOL(c:#00FF00,id:"ogr-sym-1",s:1pt);LABEL(t:"foo",s:12pt)'
    lyr.CreateFeature(f)
    f = ogr.Feature(lyr.GetLayerDefn())
    f.SetGeometryDirectly(ogr.CreateGeometryFromWkt('POINT(0 0)'))
    lyr.CreateFeature(f)
    ds = None

    out_filename = "tmp/tmp.pdf"
    out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                     options = ['COMPOSITION_FILE=' + xml_content])
    assert out_ds
    assert gdal.GetLastErrorMsg() == ''

    ogr.GetDriverByName("ESRI Shapefile").DeleteDataSource('/vsimem/test.shp')

    f = open('data/test_pdf_composition_vector_georeferenced.pdf', 'rb')
    data_ref = f.read()
    f.close()

    f = open(out_filename, 'rb')
    data_got = f.read()
    f.close()

    assert data_ref == data_got, 'content does not match reference content'

    gdal.Unlink(out_filename)


def test_pdf_composition_layer_tree_displayOnlyOnVisiblePages():

    xml_content = """<PDFComposition>
    <LayerTree displayOnlyOnVisiblePages="true">
        <Layer id="l1" name="Layer of page 1"/>
        <Layer id="l2" name="Layer of page 2"/>
    </LayerTree>
    <Page>
        <Width>20</Width>
        <Height>20</Height>
        <Content streamCompression="NONE">
            <IfLayerOn layerId="l1"/>
        </Content>
    </Page>
    <Page>
        <Width>20</Width>
        <Height>20</Height>
        <Content streamCompression="NONE">
            <IfLayerOn layerId="l2"/>
        </Content>
    </Page>
</PDFComposition>"""

    out_filename = "tmp/tmp.pdf"
    out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                     options = ['COMPOSITION_FILE=' + xml_content])
    assert out_ds
    assert gdal.GetLastErrorMsg() == ''

    f = open('data/test_pdf_composition_layer_tree_displayOnlyOnVisiblePages.pdf', 'rb')
    data_ref = f.read()
    f.close()

    f = open(out_filename, 'rb')
    data_got = f.read()
    f.close()

    assert data_ref == data_got, 'content does not match reference content'

    gdal.Unlink(out_filename)


def test_pdf_composition_outline():

    xml_content = """<PDFComposition>
    <LayerTree>
        <Layer id="foo" name="foo"/>
        <Layer id="bar" name="bar"/>
    </LayerTree>

    <Page id="1">
        <Width>20</Width>
        <Height>20</Height>
        <Content streamCompression="NONE"/>
    </Page>
    <Page id="2">
        <Width>20</Width>
        <Height>20</Height>
        <Content streamCompression="NONE"/>
    </Page>

    <Outline>
        <OutlineItem name="turn all on" italic="true">
            <Actions>
                <SetAllLayersStateAction visible="true"/>
            </Actions>
        </OutlineItem>
        <OutlineItem name="turn all off" bold="true">
            <Actions>
                <SetAllLayersStateAction visible="false"/>
            </Actions>
        </OutlineItem>
        <OutlineItem name="foo on, bar off + fullscreen">
            <Actions>
                <SetLayerStateAction visible="true" layerId="foo"/>
                <SetLayerStateAction visible="false" layerId="bar"/>
                <JavascriptAction>app.fs.isFullScreen = true;</JavascriptAction>
            </Actions>
        </OutlineItem>
        <OutlineItem name="1: page 1">
            <Actions>
                <GotoPageAction pageId="1"/>
            </Actions>
            <OutlineItem name="1.1" open="false">
                <OutlineItem name="1.1.1: Page 1">
                    <Actions>
                        <GotoPageAction pageId="1"/>
                    </Actions>
                </OutlineItem>
                <OutlineItem name="1.1.2: Page 2">
                    <Actions>
                        <GotoPageAction pageId="2"/>
                    </Actions>
                </OutlineItem>
            </OutlineItem>
            <OutlineItem name="1.2: Page 2(zoomed in) + foo on">
                <Actions>
                    <GotoPageAction pageId="2" x1="1" y1="2" x2="3" y2="4"/>
                    <SetLayerStateAction visible="true" layerId="foo"/>
                </Actions>
            </OutlineItem>
        </OutlineItem>
        <OutlineItem name="2: page 2">
            <Actions>
                <GotoPageAction pageId="2"/>
            </Actions>
        </OutlineItem>
    </Outline>

</PDFComposition>"""

    out_filename = "tmp/tmp.pdf"
    out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                     options = ['COMPOSITION_FILE=' + xml_content])
    assert out_ds
    assert gdal.GetLastErrorMsg() == ''

    f = open('data/test_pdf_composition_outline.pdf', 'rb')
    data_ref = f.read()
    f.close()

    f = open(out_filename, 'rb')
    data_got = f.read()
    f.close()

    assert data_ref == data_got, 'content does not match reference content'

    gdal.Unlink(out_filename)


def test_pdf_composition_error_missing_file():

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=/vsimem/missing.xml'])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == "Cannot open file '/vsimem/missing.xml'"
    gdal.Unlink(out_filename)


def test_pdf_composition_error_missing_page():

    xml_content = """<PDFComposition></PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == 'At least one page should be defined'
    gdal.Unlink(out_filename)


def test_pdf_composition_error_missing_page_width():

    xml_content = """<PDFComposition><Page/></PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == 'Missing or invalid Width and/or Height'
    gdal.Unlink(out_filename)


def test_pdf_composition_error_missing_page_content():

    xml_content = """<PDFComposition><Page><Width>1</Width><Height>1</Height></Page></PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == 'Missing Content'
    gdal.Unlink(out_filename)


def test_pdf_composition_error_invalid_layer_missing_id():

    xml_content = """<PDFComposition>
    <LayerTree>
        <Layer name="foo"/>
    </LayerTree>
    <Page><Width>1</Width><Height>1</Height><Content/></Page>
</PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == 'Missing id attribute in Layer'
    gdal.Unlink(out_filename)


def test_pdf_composition_error_invalid_layer_missing_name():

    xml_content = """<PDFComposition>
    <LayerTree>
        <Layer id="foo"/>
    </LayerTree>
    <Page><Width>1</Width><Height>1</Height><Content/></Page>
</PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == 'Missing name attribute in Layer'
    gdal.Unlink(out_filename)


def test_pdf_composition_error_duplicate_layer_id():

    xml_content = """<PDFComposition>
    <LayerTree>
        <Layer id="foo" name="x"/>
        <Layer id="foo" name="y"/>
    </LayerTree>
    <Page><Width>1</Width><Height>1</Height><Content/></Page>
</PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == 'Layer.id = foo is not unique'
    gdal.Unlink(out_filename)


def test_pdf_composition_error_referencing_invalid_layer_id():

    xml_content = """<PDFComposition>
    <LayerTree>
        <Layer id="foo" name="x"/>
    </LayerTree>
    <Page>
        <Width>1</Width><Height>1</Height>
        <Content>
            <IfLayerOn layerId="nonexisting"/>
        </Content>
    </Page>
</PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == 'Referencing layer of unknown id: nonexisting'
    gdal.Unlink(out_filename)


def test_pdf_composition_error_missing_srs():

    xml_content = """<PDFComposition>
    <Page>

        <Georeferencing> 
            <!--<SRS dataAxisToSRSAxisMapping="2,1">EPSG:4326</SRS> -->
            <BoundingBox x1="1" y1="1" x2="19" y2="9"/>
            <BoundingPolygon>POLYGON((1 1,19 1,19 9,1 9,1 1))</BoundingPolygon>
            <ControlPoint x="1"  y="1"  GeoY="-90"  GeoX="-180"/>
            <ControlPoint x="19" y="1"  GeoY="90"   GeoX="180"/>
            <ControlPoint x="1"  y="9"  GeoY="-90"  GeoX="-180"/>
            <ControlPoint x="19" y="9"  GeoY="90"   GeoX="180"/>
        </Georeferencing>

        <Width>1</Width><Height>1</Height>
        <Content>
        </Content>
    </Page>
</PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == 'Missing SRS'
    gdal.Unlink(out_filename)


def test_pdf_composition_error_missing_control_point():

    xml_content = """<PDFComposition>
    <Page>

        <Georeferencing> 
            <SRS dataAxisToSRSAxisMapping="2,1">EPSG:4326</SRS>
            <ControlPoint x="1"  y="1"  GeoY="-90"  GeoX="-180"/>
            <ControlPoint x="19" y="1"  GeoY="90"   GeoX="180"/>
            <ControlPoint x="1"  y="9"  GeoY="-90"  GeoX="-180"/>
            <!--<ControlPoint x="19" y="9"  GeoY="90"   GeoX="180"/>-->
        </Georeferencing>

        <Width>1</Width><Height>1</Height>
        <Content>
        </Content>
    </Page>
</PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == 'At least 4 ControlPoint are required'
    gdal.Unlink(out_filename)


def test_pdf_composition_error_missing_attribute_in_control_point():

    xml_content = """<PDFComposition>
    <Page>

        <Georeferencing> 
            <SRS dataAxisToSRSAxisMapping="2,1">EPSG:4326</SRS>
            <BoundingBox x1="1" y1="1" x2="19" y2="9"/>
            <BoundingPolygon>POLYGON((1 1,19 1,19 9,1 9,1 1))</BoundingPolygon>
            <ControlPoint x="1"  y="1"  GeoY="-90"  GeoX="-180"/>
            <ControlPoint x="19" y="1"  GeoY="90"   GeoX="180"/>
            <ControlPoint x="1"  y="9"  GeoY="-90"  GeoX="-180"/>
            <ControlPoint x="19" y="9"  GeoY="90"   missing___GeoX="180"/>
        </Georeferencing>

        <Width>1</Width><Height>1</Height>
        <Content>
        </Content>
    </Page>
</PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == 'At least one of x, y, GeoX or GeoY attribute missing on ControlPoint'
    gdal.Unlink(out_filename)


def test_pdf_composition_error_invalid_bbox():

    xml_content = """<PDFComposition>
    <Page>

        <Georeferencing> 
            <SRS dataAxisToSRSAxisMapping="2,1">EPSG:4326</SRS>
            <BoundingBox x1="1" y1="1" x2="1" y2="9"/>
            <BoundingPolygon>POLYGON((1 1,19 1,19 9,1 9,1 1))</BoundingPolygon>
            <ControlPoint x="1"  y="1"  GeoY="-90"  GeoX="-180"/>
            <ControlPoint x="19" y="1"  GeoY="90"   GeoX="180"/>
            <ControlPoint x="1"  y="9"  GeoY="-90"  GeoX="-180"/>
            <ControlPoint x="19" y="9"  GeoY="90"   GeoX="180"/>
        </Georeferencing>

        <Width>1</Width><Height>1</Height>
        <Content>
        </Content>
    </Page>
</PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == 'Invalid BoundingBox'
    gdal.Unlink(out_filename)


def test_pdf_composition_error_missing_dataset_attribute():

    xml_content = """<PDFComposition>
    <Page>
        <Width>1</Width><Height>1</Height>
        <Content>
            <Raster/>
        </Content>
    </Page>
</PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == 'Missing dataset'
    gdal.Unlink(out_filename)


def test_pdf_composition_error_invalid_dataset():

    xml_content = """<PDFComposition>
    <Page>
        <Width>1</Width><Height>1</Height>
        <Content>
            <Raster dataset="non_existing"/>
        </Content>
    </Page>
</PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() != ''
    gdal.Unlink(out_filename)


def test_pdf_composition_duplicate_page_id():

    xml_content = """<PDFComposition>
    <Page id="1">
        <Width>20</Width>
        <Height>20</Height>
        <Content streamCompression="NONE"/>
    </Page>
    <Page id="1">
        <Width>20</Width>
        <Height>20</Height>
        <Content streamCompression="NONE"/>
    </Page>

</PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == 'Duplicated page id 1'
    gdal.Unlink(out_filename)


def test_pdf_composition_outline_item_gotopage_action_missing_page_id():

    xml_content = """<PDFComposition>
    <Page id="1">
        <Width>20</Width>
        <Height>20</Height>
        <Content streamCompression="NONE"/>
    </Page>

    <Outline>
        <OutlineItem name="name">
            <Actions>
                <GotoPageAction/>
            </Actions>
        </OutlineItem>
    </Outline>

</PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == 'Missing pageId attribute in GotoPageAction'
    gdal.Unlink(out_filename)


def test_pdf_composition_outline_item_gotopage_action_pointing_to_invalid_page_id():

    xml_content = """<PDFComposition>
    <Page id="1">
        <Width>20</Width>
        <Height>20</Height>
        <Content streamCompression="NONE"/>
    </Page>

    <Outline>
        <OutlineItem name="name">
            <Actions>
                <GotoPageAction pageId="non_existing"/>
            </Actions>
        </OutlineItem>
    </Outline>

</PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == 'GotoPageAction.pageId = non_existing not pointing to a Page.id'
    gdal.Unlink(out_filename)


def test_pdf_composition_outline_item_setlayerstate_missing_layer_id():

    xml_content = """<PDFComposition>
    <Page id="1">
        <Width>20</Width>
        <Height>20</Height>
        <Content streamCompression="NONE"/>
    </Page>

    <Outline>
        <OutlineItem name="name">
            <Actions>
                <SetLayerStateAction visible="true"/>
            </Actions>
        </OutlineItem>
    </Outline>

</PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == 'Missing layerId'
    gdal.Unlink(out_filename)


def test_pdf_composition_outline_item_setlayerstate_pointing_to_invalid_layer_id():

    xml_content = """<PDFComposition>
    <Page id="1">
        <Width>20</Width>
        <Height>20</Height>
        <Content streamCompression="NONE"/>
    </Page>

    <Outline>
        <OutlineItem name="name">
            <Actions>
                <SetLayerStateAction layerId="non_existing" visible="true"/>
            </Actions>
        </OutlineItem>
    </Outline>

</PDFComposition>"""

    out_filename = "/vsimem/tmp.pdf"
    with gdaltest.error_handler():
        out_ds = gdaltest.pdf_drv.Create(out_filename, 0, 0, 0, gdal.GDT_Unknown,
                                         options = ['COMPOSITION_FILE=' + xml_content])
    assert not out_ds
    assert gdal.GetLastErrorMsg() == 'Referencing layer of unknown id: non_existing'
    gdal.Unlink(out_filename)
