new N($value), [1000,0,0,0,1000,0,0,0,1,1000,2000,0])); $projectedMeasure = new G\GeospatialMeasure($projected, new G\GeospatialControlPoints(...$projectedPoints), $bounds, displayUnits: $units, matrix: $pcsm); // 2. A point cloud retains its own column names and unordered tuples. These // illustrative positions are data, independently of how the page is drawn. $cloud = new G\PointData(new G\PointDataNames(new NameType('LAT'), new NameType('LON'), new NameType('ALT')), new ArrayType(new N(48.86), new N(2.35), new N(35))); $clouds = new G\PointDataCollection($cloud); $left = new Rectangle(new N(48), new N(350), new N(284), new N(620)); $right = new Rectangle(new N(310), new N(350), new N(546), new N(620)); $viewports = new G\Viewports(new G\Viewport($left, $geoMeasure, new TextString('Geographic illustration'), $clouds), new G\Viewport($right, $projectedMeasure, new TextString('Projected illustration'))); // 3. GEO measures also belong directly to form and image XObjects. Their LPTS // use the object's unit square, independently of the page's drawing matrix. $grid = [new Op\SetStrokingRGBColor(new N(0.15), new N(0.42), new N(0.55)), new Op\SetLineWidth(new N(0.004)), new Op\AppendRectangle(new N(0), new N(0), new N(1), new N(1)), new Op\StrokePath()]; foreach ([0.2,0.4,0.6,0.8] as $position) { array_push($grid, new Op\MoveTo(new N($position), new N(0)), new Op\LineTo(new N($position), new N(1)), new Op\MoveTo(new N(0), new N($position)), new Op\LineTo(new N(1), new N($position)), new Op\StrokePath()); } $form = new FormXObject(new ContentStreams(...$grid), new Rectangle(new N(0), new N(0), new N(1), new N(1)), new ResourceDictionary(), extensions: [new Entry(new NameType('Measure'), $geoMeasure), new Entry(new NameType('PtData'), $cloud)]); $image = new ImageXObject(new ImageSamples(new ImageDimensions(1, 1), new SampleLayout(DeviceColorSpace::RGB), "\xe3\xe9\xd4"), options: new ImageOptions(extensions: [new Entry(new NameType('Measure'), $projectedMeasure)])); // PDF 2.0 requires explicit metrics and a descriptor even for standard fonts. $font = require __DIR__ . '/fixtures/pdf20-courier-font.php'; $resources = new ResourceDictionary(new Font(new DictionaryType(new Entry(new NameType('F1'), new IndirectReference($font)))), new XObject(new DictionaryType(new Entry(new NameType('Grid'), new IndirectReference($form)), new Entry(new NameType('Raster'), new IndirectReference($image))))); $operators = []; foreach ([['PDF 2.0: geospatial objects',24,48,778], ['Explicit coordinate systems, point pairs and measurement viewports.',11,48,746], ['Geographic / EPSG 4326',12,48,648], ['Projected / EPSG 3857',12,310,648], ['Latitude, longitude',11,48,327], ['Easting, northing',11,310,327], ['GEO metadata is independent of the page illustration.',12,48,278], ['The left form and right raster carry their own GEO measures.',11,48,248], ['A LAT / LON / ALT cloud records the position 48.86, 2.35, 35.',11,48,223], ['PCSM records an explicit affine transformation for projected coordinates.',11,48,198], ['These independent sample regions are illustrative, not a surveyed map.',10,48,143], ['ISO 32000-2:2020, clauses 12.9 and 12.10; selected EC3 corrections.',10,48,118]] as [$text, $size, $x, $y]) { array_push($operators, new Op\BeginTextObject(), new Op\SetTextFontAndSize(new NameType('F1'), new N($size)), new Op\MoveTextPosition(new N($x), new N($y)), new Op\ShowText(new LiteralString($text)), new Op\EndTextObject()); } array_push($operators, new Op\SaveGraphicsState(), new Op\ConcatenateMatrix(new Matrix(new N(236), new N(0), new N(0), new N(270), new N(48), new N(350))), new Op\InvokeXObject(new NameType('Grid')), new Op\RestoreGraphicsState(), new Op\SaveGraphicsState(), new Op\ConcatenateMatrix(new Matrix(new N(236), new N(0), new N(0), new N(270), new N(310), new N(350))), new Op\InvokeXObject(new NameType('Raster')), new Op\RestoreGraphicsState()); $page = new PageObject(new Rectangle(new N(0), new N(0), new N(595), new N(842)), $resources, new StreamObject(new ContentStreams(...$operators)), new Entry(new NameType('VP'), $viewports)); $catalog = new DocumentCatalog((new PageTreeNode())->addKid($page)); $file = new FileStructure(new FileHeader('2.0'), new FileBody($catalog)); $path = $argv[1] ?? __DIR__ . '/pdf20-geospatial.pdf'; $file->writeTo($path); echo 'PDF written to ' . $path . PHP_EOL;