diff --git a/DemoApp/DemoApp.csproj b/DemoApp/DemoApp.csproj new file mode 100644 index 0000000..dc7fd3f --- /dev/null +++ b/DemoApp/DemoApp.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + diff --git a/DemoApp/Program.cs b/DemoApp/Program.cs new file mode 100644 index 0000000..f0ef2eb --- /dev/null +++ b/DemoApp/Program.cs @@ -0,0 +1,203 @@ +// DemoApp – exercises all README examples to verify they compile and run. + +using Pmad.Cartography; +using Pmad.Cartography.Contours; +using Pmad.Cartography.Databases; +using Pmad.Cartography.DataCells; +using Pmad.Cartography.Drawing.Contours; +using Pmad.Cartography.Drawing.Topographic; +using Pmad.Cartography.Hillshading; +using Pmad.Cartography.Projections; +using Pmad.Drawing; +using Pmad.Drawing.PdfRender; +using Pmad.Geometry; +using Pmad.ProgressTracking; +using SixLabors.ImageSharp; + +// ───────────────────────────────────────────────────────────────────────────── +// Pmad.Cartography README examples +// ───────────────────────────────────────────────────────────────────────────── + +Console.WriteLine("=== Pmad.Cartography ==="); + +// Query elevation from a well-known database +// Uses SRTM1 data hosted on cdn.dem.pmad.net (downloaded on demand and cached locally) +var database = WellKnownDatabases.GetSRTM1(); + +// Single point – bilinear interpolation +var elevation = await database.GetElevationAsync( + new Coordinates(51.509865, -0.118092), + DefaultInterpolation.Instance); +Console.WriteLine($"Elevation at London: {elevation:F1} m"); + +// Load an area into memory as a float raster +var area = await database.CreateView( + new Coordinates(51, -1), + new Coordinates(52, 0)); +Console.WriteLine($"Area loaded: {area.PointsLon}x{area.PointsLat} px"); + +// Contour lines +var contour = new ContourGraph(); +// 10-metre interval starting at 10 m +contour.Add(area, new ContourLevelGenerator(10, 10)); + +foreach (var line in contour.Lines.Take(3)) +{ + Console.WriteLine($"Level {line.Level}: {line.Points.Count} points"); +} + +// Hillshading – each pixel represents a 10x10-metre cell +var hillshader = new HillshaderFast(new Vector2D(10, 10)); + +// Returns an image where alpha encodes shadow intensity +var hillshadeImg = hillshader.GetPixelsAlphaBelowFlat(area); +Console.WriteLine($"Hillshade image: {hillshadeImg.Width}x{hillshadeImg.Height}"); + +// Custom cache path example (not actually downloaded here) +var _ = WellKnownDatabases.GetSRTM1(localCache: Path.Combine(Path.GetTempPath(), "dem-cache")); + +// ───────────────────────────────────────────────────────────────────────────── +// Pmad.Drawing README examples +// ───────────────────────────────────────────────────────────────────────────── + +Console.WriteLine("\n=== Pmad.Drawing ==="); + +var myPoints = new Vector2D[] { new(10, 10), new(200, 10), new(200, 200), new(10, 200) }; +var myLine = new Vector2D[] { new(50, 50), new(400, 300), new(700, 100) }; + +// Render to SVG +Render.ToSvg("output.svg", new Vector2D(800, 600), surface => +{ + var fill = new SolidColorBrush(Color.LightBlue); + var stroke = new Pen(new SolidColorBrush(Color.DarkBlue), 2); + var style = surface.AllocateStyle(fill, stroke); + + surface.DrawPolygon(new[] { myPoints }, style); + surface.DrawPolyline(myLine, style); +}); +Console.WriteLine("SVG written: output.svg"); + +// Render to PNG +Render.ToPng("output.png", new Vector2D(800, 600), surface => +{ + // same drawing calls as SVG + var fill = new SolidColorBrush(Color.LightBlue); + var stroke = new Pen(new SolidColorBrush(Color.DarkBlue), 2); + var style = surface.AllocateStyle(fill, stroke); + surface.DrawPolygon(new[] { myPoints }, style); + surface.DrawPolyline(myLine, style); +}); +Console.WriteLine("PNG written: output.png"); + +// Render to PDF (sizeInPixels, not a PaperSize enum) +Render.ToPdf("output.pdf", new Vector2D(800, 600), surface => +{ + // same drawing calls + var fill = new SolidColorBrush(Color.LightBlue); + var stroke = new Pen(new SolidColorBrush(Color.DarkBlue), 2); + var style = surface.AllocateStyle(fill, stroke); + surface.DrawPolygon(new[] { myPoints }, style); +}); +Console.WriteLine("PDF written: output.pdf"); + +// Generate map tiles (Leaflet-compatible) +TilingInfos info = Render.ToSvgTiled( + "tiles/map.svg", + new Vector2D(1024, 1024), + SvgFallBackFormats.Webp, + drawLod1: surface => + { + var style = surface.AllocateStyle(new SolidColorBrush(Color.LightGreen), null); + surface.DrawPolygon(new[] { myPoints }, style); + }, + drawLod2: surface => + { + var style = surface.AllocateStyle(new SolidColorBrush(Color.Green), null); + surface.DrawPolygon(new[] { myPoints }, style); + }); +Console.WriteLine($"Tiles written – zoom {info.MinZoom}–{info.MaxZoom}"); + +// Tile an existing Image directly +using var fullImage = new SixLabors.ImageSharp.Image(512, 512); +TilingInfos info2 = ImageTiler.DefaultToWebp(fullImage, "tiles-img/"); +Console.WriteLine($"Image tiles written – zoom {info2.MinZoom}–{info2.MaxZoom}"); + +// ───────────────────────────────────────────────────────────────────────────── +// Pmad.Cartography.Drawing README examples +// ───────────────────────────────────────────────────────────────────────────── + +Console.WriteLine("\n=== Pmad.Cartography.Drawing ==="); + +// Projection area covering the loaded DEM view +var proj = new NoProjectionArea( + new Coordinates(51, -1), + new Coordinates(52, 0), + new Vector2D(1024, 1024)); + +// Render contour lines +Render.ToSvg("contours.svg", proj.Size, surface => +{ + var renderer = new ContourRender(surface); + renderer.Render(contour, proj, hillshadeImg); +}); +Console.WriteLine("Contour SVG written: contours.svg"); + +// Topographic map rendering +using var scope = new NoProgress(); + +// Build a minimal ITopoMapData implementation using the DEM view we already loaded +var topoData = new DemoTopoMapData(area); + +var renderData = TopoMapRenderData.Create(topoData, scope); + +Render.ToSvgTiled("topo/map.svg", proj.Size, SvgFallBackFormats.Webp, + drawLod1: surface => new TopoMapRender(renderData, proj).Render(surface), + drawLod2: surface => new TopoMapRender(renderData, proj).RenderLod2(surface), + drawLod3: surface => new TopoMapRender(renderData, proj).RenderLod3(surface)); +Console.WriteLine("Topographic tiled SVG written: topo/map.svg"); + +// PDF export +// Note: TopoMapPdfRender is designed for metric units, but the demo data is in degrees, so rescale to have something useful +topoData = new DemoTopoMapData(new DemDataCellPixelIsPoint(new Coordinates(0,0), new Coordinates(10240, 10240), area.ToDataCell().Data)); + +System.IO.Directory.CreateDirectory("topo-pdf"); +var pdfFiles = TopoMapPdfRender.RenderPDF("topo-pdf", "demo", topoData, scope); +Console.WriteLine($"PDF files written: {pdfFiles.Count}"); + +// Map metadata example +var metadata = new TopoMapMetadata( + attribution: "Map data (c) OpenStreetMap contributors", + title: "My Topographic Map", + licenseNotice: "CC BY-SA 4.0", + exportCreator: "DemoApp 1.0", + upperTitle: "Sheet 1"); +Console.WriteLine($"Metadata title: {metadata.Title}"); + +Console.WriteLine("\nAll examples completed successfully."); + +// ───────────────────────────────────────────────────────────────────────────── +// Minimal ITopoMapData implementation used by the demo +// ───────────────────────────────────────────────────────────────────────────── + +internal sealed class DemoTopoMapData : ITopoMapData +{ + public DemoTopoMapData(Pmad.Cartography.DataCells.IDemDataView dem) + { + DemDataCell = dem; + } + + public TopoMapMetadata Metadata => TopoMapMetadata.None; + public Pmad.Cartography.DataCells.IDemDataView DemDataCell { get; } + public Dictionary>? Roads => null; + public Dictionary>? Bridges => null; + public Pmad.Geometry.Shapes.MultiPolygon? ForestPolygons => null; + public Pmad.Geometry.Shapes.MultiPolygon? RockPolygons => null; + public Pmad.Geometry.Shapes.MultiPolygon? BuildingPolygons => null; + public Pmad.Geometry.Shapes.MultiPolygon? WaterPolygons => null; + public Pmad.Geometry.Shapes.MultiPolygon? FortPolygons => null; + public List? Names => null; + public List? Icons => null; + public Pmad.Geometry.Shapes.MultiPath? Powerlines => null; + public Pmad.Geometry.Shapes.MultiPath? Railways => null; + public List? PlottedPoints => null; +} diff --git a/MapToolkit.Test/Databases/HttpClientHelperTest.cs b/MapToolkit.Test/Databases/HttpClientHelperTest.cs new file mode 100644 index 0000000..7efa662 --- /dev/null +++ b/MapToolkit.Test/Databases/HttpClientHelperTest.cs @@ -0,0 +1,71 @@ +using System; +using System.Linq; +using System.Net.Http; +using Pmad.Cartography.Databases; + +namespace Pmad.Cartography.Test.Databases +{ + public class HttpClientHelperTest + { + private const string BaseAddressString = "https://cdn.dem.pmad.net/SRTM1/"; + private static readonly Uri BaseAddressUri = new Uri(BaseAddressString); + + [Fact] + public void CreateClient_WithUri_SetsBaseAddress() + { + using var client = HttpClientHelper.CreateClient(BaseAddressUri); + + Assert.Equal(BaseAddressUri, client.BaseAddress); + } + + [Fact] + public void CreateClient_WithString_SetsBaseAddress() + { + using var client = HttpClientHelper.CreateClient(BaseAddressString); + + Assert.Equal(BaseAddressUri, client.BaseAddress); + } + + [Fact] + public void CreateClient_WithUri_SetsUserAgentHeader() + { + using var client = HttpClientHelper.CreateClient(BaseAddressUri); + + var userAgentValues = client.DefaultRequestHeaders.UserAgent.ToList(); + Assert.Equal(2, userAgentValues.Count); + Assert.Equal("Mozilla", userAgentValues[0].Product?.Name); + Assert.Equal("5.0", userAgentValues[0].Product?.Version); + Assert.Equal("(Pmad-Cartography; Default)", userAgentValues[1].Comment); + } + + [Fact] + public void CreateClient_WithString_SetsUserAgentHeader() + { + using var client = HttpClientHelper.CreateClient(BaseAddressString); + + var userAgentValues = client.DefaultRequestHeaders.UserAgent.ToList(); + Assert.Equal(2, userAgentValues.Count); + Assert.Equal("Mozilla", userAgentValues[0].Product?.Name); + Assert.Equal("5.0", userAgentValues[0].Product?.Version); + Assert.Equal("(Pmad-Cartography; Default)", userAgentValues[1].Comment); + } + + [Fact] + public void CreateClient_ReturnsDifferentInstances() + { + using var client1 = HttpClientHelper.CreateClient(BaseAddressUri); + using var client2 = HttpClientHelper.CreateClient(BaseAddressUri); + + Assert.NotSame(client1, client2); + } + + [Fact] + public void CreateClient_WithString_AndWithUri_ProduceSameBaseAddress() + { + using var clientFromString = HttpClientHelper.CreateClient(BaseAddressString); + using var clientFromUri = HttpClientHelper.CreateClient(BaseAddressUri); + + Assert.Equal(clientFromString.BaseAddress, clientFromUri.BaseAddress); + } + } +} diff --git a/MapToolkit/Databases/DemHttpStorage.cs b/MapToolkit/Databases/DemHttpStorage.cs index 465c1d9..391b93d 100644 --- a/MapToolkit/Databases/DemHttpStorage.cs +++ b/MapToolkit/Databases/DemHttpStorage.cs @@ -8,10 +8,20 @@ namespace Pmad.Cartography.Databases { + /// + /// An implementation that downloads DEM tiles on demand from an HTTP + /// server and caches them locally on disk. + /// + /// + /// Downloaded tiles are stored under (or a custom path supplied + /// to the constructor) and reused on subsequent requests, so network access only occurs when a + /// tile is not yet present in the cache. + /// public class DemHttpStorage : IDemStorage { /// - /// How long a cached index.json is considered fresh before being re-downloaded. + /// Gets or sets how long a cached index.json is considered fresh before being + /// re-downloaded. Defaults to 24 hours. /// public static TimeSpan IndexCacheDuration { get; set; } = TimeSpan.FromHours(24); @@ -20,26 +30,60 @@ public class DemHttpStorage : IDemStorage private readonly string localCache; private readonly HttpClient client; + /// + /// Initialises a new instance of with an already-configured + /// . + /// + /// + /// Directory used to cache downloaded tiles. Pass to use + /// . + /// + /// + /// An whose points to the DEM + /// server root. + /// public DemHttpStorage(string? localCache, HttpClient client) { this.localCache = localCache ?? DefaultCacheLocation; this.client = client; } + /// + /// Initialises a new instance of that creates its own + /// for the given base address. + /// + /// + /// Directory used to cache downloaded tiles. Pass to use + /// . + /// + /// Base URI of the DEM HTTP server. public DemHttpStorage(string? localCache, Uri baseAddress) - : this(localCache, new HttpClient() { BaseAddress = baseAddress }) + : this(localCache, HttpClientHelper.CreateClient(baseAddress)) { } + /// + /// Initialises a new instance of using + /// as the local cache directory. + /// + /// Base URI of the DEM HTTP server. public DemHttpStorage(Uri baseAddress) : this(null, baseAddress) { } + /// + /// Gets the default directory used to cache downloaded tiles when no explicit path is + /// provided. Resolves to a dem sub-directory inside the system temporary folder. + /// public static string DefaultCacheLocation => Path.Combine(Path.GetTempPath(), "dem"); + /// + /// Deletes all files in , freeing disk space occupied by + /// previously downloaded tiles. Has no effect when the directory does not exist. + /// public static void ClearDefaultCache() { var cacheDir = DefaultCacheLocation; @@ -55,6 +99,21 @@ private string GetCacheFile(string path) return Path.Combine(localCache, uri.DnsSafeHost, uri.AbsolutePath.Substring(1).Replace('/', Path.DirectorySeparatorChar)); } + /// + /// Loads a DEM tile from the cache, downloading it first if necessary. + /// + /// Path of the tile relative to the configured dataset root (e.g. N00E006.SRTMGL1.hgt.zst). + /// + /// Optional expected SHA-256 hex digest. When provided the cached file is verified after + /// download; a mismatch causes the file to be deleted and an + /// to be thrown. + /// + /// Token to cancel the asynchronous operation. + /// The loaded . + /// + /// Thrown when is set and the downloaded file does not + /// match the expected digest. + /// public async Task LoadAsync(string path, string? expectedSha256 = null, CancellationToken cancellationToken = default) { var cacheFile = GetCacheFile(path); @@ -117,8 +176,14 @@ private async Task DownloadFile(string path, string cacheFile, CancellationToken } } + /// public Task Load(string path) => LoadAsync(path, null); + /// + /// Downloads (if required) and deserialises the server's index.json file. + /// The cached copy is reused as long as it is younger than . + /// + /// The deserialized . public async Task ReadIndex() { var cacheFile = GetCacheFile("index.json"); @@ -134,6 +199,16 @@ public async Task ReadIndex() } } + /// + /// Downloads the file at and returns its SHA-256 hex digest, or + /// if the file does not exist on the server. + /// + /// Server-relative path of the file to hash. + /// Token to cancel the asynchronous operation. + /// + /// A lowercase hex string representing the SHA-256 digest, or when + /// the server returns HTTP 404. + /// public async Task GetSha256Async(string path, CancellationToken cancellationToken = default) { var cacheFile = GetCacheFile(path); diff --git a/MapToolkit/Databases/HttpClientHelper.cs b/MapToolkit/Databases/HttpClientHelper.cs new file mode 100644 index 0000000..7a9d9cc --- /dev/null +++ b/MapToolkit/Databases/HttpClientHelper.cs @@ -0,0 +1,22 @@ +using System; +using System.Net.Http; + +namespace Pmad.Cartography.Databases +{ + internal static class HttpClientHelper + { + private const string DefaultUserAgent = "Mozilla/5.0 (Pmad-Cartography; Default)"; + + internal static HttpClient CreateClient(string baseAddress) + { + return CreateClient(new Uri(baseAddress)); + } + + internal static HttpClient CreateClient(Uri baseAddress) + { + var httpClient = new HttpClient { BaseAddress = baseAddress }; + httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(DefaultUserAgent); + return httpClient; + } + } +} diff --git a/MapToolkit/Databases/WellKnownDatabases.cs b/MapToolkit/Databases/WellKnownDatabases.cs index 61ec130..4516cac 100644 --- a/MapToolkit/Databases/WellKnownDatabases.cs +++ b/MapToolkit/Databases/WellKnownDatabases.cs @@ -1,20 +1,7 @@ -using System; -using System.Net.Http; - -namespace Pmad.Cartography.Databases +namespace Pmad.Cartography.Databases { public static class WellKnownDatabases { - private const string DefaultUserAgent = "Mozilla/5.0 (Pmad-Cartography; Default)"; - - internal static HttpClient CreateClient(string baseAddress) - { - var httpClient = new HttpClient { BaseAddress = new Uri(baseAddress) }; - // OVH CDN Requires a user agent - httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(DefaultUserAgent); - return httpClient; - } - /// /// Get the AW3D 30 data hosted on cdn.dem.pmad.net: /// Japan Aerospace Exploration Agency (2021). ALOS World 3D 30 meter DEM. V3.2, Jan 2021. @@ -29,7 +16,7 @@ internal static HttpClient CreateClient(string baseAddress) /// public static DemHttpStorage GetAW3D30Storage(string? localCache = null) { - return new DemHttpStorage(localCache, CreateClient("https://cdn.dem.pmad.net/AW3D30/")); + return new DemHttpStorage(localCache, HttpClientHelper.CreateClient("https://cdn.dem.pmad.net/AW3D30/")); } /// @@ -43,7 +30,7 @@ public static DemHttpStorage GetAW3D30Storage(string? localCache = null) /// public static DemHttpStorage GetSRTM1Storage(string? localCache = null) { - return new DemHttpStorage(localCache, CreateClient("https://cdn.dem.pmad.net/SRTM1/")); + return new DemHttpStorage(localCache, HttpClientHelper.CreateClient("https://cdn.dem.pmad.net/SRTM1/")); } /// @@ -58,7 +45,7 @@ public static DemHttpStorage GetSRTM1Storage(string? localCache = null) /// public static DemHttpStorage GetSRTM15PlusStorage(string? localCache = null) { - return new DemHttpStorage(localCache, CreateClient("https://cdn.dem.pmad.net/SRTM15Plus/")); + return new DemHttpStorage(localCache, HttpClientHelper.CreateClient("https://cdn.dem.pmad.net/SRTM15Plus/")); } /// diff --git a/Pmad.Cartography.Drawing/README.md b/Pmad.Cartography.Drawing/README.md index b5bebf7..edf96ea 100644 --- a/Pmad.Cartography.Drawing/README.md +++ b/Pmad.Cartography.Drawing/README.md @@ -32,25 +32,25 @@ Render.ToSvg("contours.svg", new Vector2D(1024, 1024), surface => ```csharp // Populate map data (roads, forests, water, buildings, etc.) -var data = new TopoMapRenderData -{ - Data = myTopoMapData, // implements ITopoMapData - Img = hillshadeImage // optional pre-computed hillshade -}; +// myTopoMapData implements ITopoMapData +using var scope = new NoProgress(); +var data = TopoMapRenderData.Create(myTopoMapData, scope); -var proj = new NoProjectionArea(origin, new Vector2D(width, height), scale); +var proj = new NoProjectionArea(min, max, new Vector2D(width, height)); Render.ToSvgTiled("map.svg", proj.Size, SvgFallBackFormats.Webp, - lod1: surface => new TopoMapRender(data, proj).Render(surface), - lod2: surface => new TopoMapRender(data, proj).RenderLod2(surface), - lod3: surface => new TopoMapRender(data, proj).RenderLod3(surface)); + drawLod1: surface => new TopoMapRender(data, proj).Render(surface), + drawLod2: surface => new TopoMapRender(data, proj).RenderLod2(surface), + drawLod3: surface => new TopoMapRender(data, proj).RenderLod3(surface)); ``` ### Export as PDF ```csharp -var pdfRender = new TopoMapPdfRender(data, proj); -pdfRender.Render("map.pdf"); +// myTopoMapData implements ITopoMapData +System.IO.Directory.CreateDirectory("output-dir"); +using var scope = new NoProgress(); +var pdfFiles = TopoMapPdfRender.RenderPDF("output-dir", "map", myTopoMapData, scope); ``` ## Map data model diff --git a/Pmad.Cartography.sln b/Pmad.Cartography.sln index 542dd9b..0a511a3 100644 --- a/Pmad.Cartography.sln +++ b/Pmad.Cartography.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.2.32616.157 +# Visual Studio Version 18 +VisualStudioVersion = 18.5.11723.231 stable MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pmad.Cartography", "MapToolkit\Pmad.Cartography.csproj", "{A7C154E7-F4A6-49AD-91DD-C1CF32650B50}" EndProject @@ -27,40 +27,114 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pmad.Cartography.Drawing.Te EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{1BBD068A-5F5B-49FE-B391-B762ECDB31C9}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DemoApp", "DemoApp\DemoApp.csproj", "{DD594970-E132-4712-9963-AC8979369DA0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A7C154E7-F4A6-49AD-91DD-C1CF32650B50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A7C154E7-F4A6-49AD-91DD-C1CF32650B50}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7C154E7-F4A6-49AD-91DD-C1CF32650B50}.Debug|x64.ActiveCfg = Debug|Any CPU + {A7C154E7-F4A6-49AD-91DD-C1CF32650B50}.Debug|x64.Build.0 = Debug|Any CPU + {A7C154E7-F4A6-49AD-91DD-C1CF32650B50}.Debug|x86.ActiveCfg = Debug|Any CPU + {A7C154E7-F4A6-49AD-91DD-C1CF32650B50}.Debug|x86.Build.0 = Debug|Any CPU {A7C154E7-F4A6-49AD-91DD-C1CF32650B50}.Release|Any CPU.ActiveCfg = Release|Any CPU {A7C154E7-F4A6-49AD-91DD-C1CF32650B50}.Release|Any CPU.Build.0 = Release|Any CPU + {A7C154E7-F4A6-49AD-91DD-C1CF32650B50}.Release|x64.ActiveCfg = Release|Any CPU + {A7C154E7-F4A6-49AD-91DD-C1CF32650B50}.Release|x64.Build.0 = Release|Any CPU + {A7C154E7-F4A6-49AD-91DD-C1CF32650B50}.Release|x86.ActiveCfg = Release|Any CPU + {A7C154E7-F4A6-49AD-91DD-C1CF32650B50}.Release|x86.Build.0 = Release|Any CPU {76B611A8-6A27-4338-8F84-BA4C74F2E518}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {76B611A8-6A27-4338-8F84-BA4C74F2E518}.Debug|Any CPU.Build.0 = Debug|Any CPU + {76B611A8-6A27-4338-8F84-BA4C74F2E518}.Debug|x64.ActiveCfg = Debug|Any CPU + {76B611A8-6A27-4338-8F84-BA4C74F2E518}.Debug|x64.Build.0 = Debug|Any CPU + {76B611A8-6A27-4338-8F84-BA4C74F2E518}.Debug|x86.ActiveCfg = Debug|Any CPU + {76B611A8-6A27-4338-8F84-BA4C74F2E518}.Debug|x86.Build.0 = Debug|Any CPU {76B611A8-6A27-4338-8F84-BA4C74F2E518}.Release|Any CPU.ActiveCfg = Release|Any CPU {76B611A8-6A27-4338-8F84-BA4C74F2E518}.Release|Any CPU.Build.0 = Release|Any CPU + {76B611A8-6A27-4338-8F84-BA4C74F2E518}.Release|x64.ActiveCfg = Release|Any CPU + {76B611A8-6A27-4338-8F84-BA4C74F2E518}.Release|x64.Build.0 = Release|Any CPU + {76B611A8-6A27-4338-8F84-BA4C74F2E518}.Release|x86.ActiveCfg = Release|Any CPU + {76B611A8-6A27-4338-8F84-BA4C74F2E518}.Release|x86.Build.0 = Release|Any CPU {F797D011-4BE7-435F-9F5F-0E52140B787A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F797D011-4BE7-435F-9F5F-0E52140B787A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F797D011-4BE7-435F-9F5F-0E52140B787A}.Debug|x64.ActiveCfg = Debug|Any CPU + {F797D011-4BE7-435F-9F5F-0E52140B787A}.Debug|x64.Build.0 = Debug|Any CPU + {F797D011-4BE7-435F-9F5F-0E52140B787A}.Debug|x86.ActiveCfg = Debug|Any CPU + {F797D011-4BE7-435F-9F5F-0E52140B787A}.Debug|x86.Build.0 = Debug|Any CPU {F797D011-4BE7-435F-9F5F-0E52140B787A}.Release|Any CPU.ActiveCfg = Release|Any CPU {F797D011-4BE7-435F-9F5F-0E52140B787A}.Release|Any CPU.Build.0 = Release|Any CPU + {F797D011-4BE7-435F-9F5F-0E52140B787A}.Release|x64.ActiveCfg = Release|Any CPU + {F797D011-4BE7-435F-9F5F-0E52140B787A}.Release|x64.Build.0 = Release|Any CPU + {F797D011-4BE7-435F-9F5F-0E52140B787A}.Release|x86.ActiveCfg = Release|Any CPU + {F797D011-4BE7-435F-9F5F-0E52140B787A}.Release|x86.Build.0 = Release|Any CPU {CD89FBC4-5213-4908-9B77-421C73CFFB6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CD89FBC4-5213-4908-9B77-421C73CFFB6D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CD89FBC4-5213-4908-9B77-421C73CFFB6D}.Debug|x64.ActiveCfg = Debug|Any CPU + {CD89FBC4-5213-4908-9B77-421C73CFFB6D}.Debug|x64.Build.0 = Debug|Any CPU + {CD89FBC4-5213-4908-9B77-421C73CFFB6D}.Debug|x86.ActiveCfg = Debug|Any CPU + {CD89FBC4-5213-4908-9B77-421C73CFFB6D}.Debug|x86.Build.0 = Debug|Any CPU {CD89FBC4-5213-4908-9B77-421C73CFFB6D}.Release|Any CPU.ActiveCfg = Release|Any CPU {CD89FBC4-5213-4908-9B77-421C73CFFB6D}.Release|Any CPU.Build.0 = Release|Any CPU + {CD89FBC4-5213-4908-9B77-421C73CFFB6D}.Release|x64.ActiveCfg = Release|Any CPU + {CD89FBC4-5213-4908-9B77-421C73CFFB6D}.Release|x64.Build.0 = Release|Any CPU + {CD89FBC4-5213-4908-9B77-421C73CFFB6D}.Release|x86.ActiveCfg = Release|Any CPU + {CD89FBC4-5213-4908-9B77-421C73CFFB6D}.Release|x86.Build.0 = Release|Any CPU {789FFCD4-375D-4201-BCEA-776B3A8D2EAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {789FFCD4-375D-4201-BCEA-776B3A8D2EAB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {789FFCD4-375D-4201-BCEA-776B3A8D2EAB}.Debug|x64.ActiveCfg = Debug|Any CPU + {789FFCD4-375D-4201-BCEA-776B3A8D2EAB}.Debug|x64.Build.0 = Debug|Any CPU + {789FFCD4-375D-4201-BCEA-776B3A8D2EAB}.Debug|x86.ActiveCfg = Debug|Any CPU + {789FFCD4-375D-4201-BCEA-776B3A8D2EAB}.Debug|x86.Build.0 = Debug|Any CPU {789FFCD4-375D-4201-BCEA-776B3A8D2EAB}.Release|Any CPU.ActiveCfg = Release|Any CPU {789FFCD4-375D-4201-BCEA-776B3A8D2EAB}.Release|Any CPU.Build.0 = Release|Any CPU + {789FFCD4-375D-4201-BCEA-776B3A8D2EAB}.Release|x64.ActiveCfg = Release|Any CPU + {789FFCD4-375D-4201-BCEA-776B3A8D2EAB}.Release|x64.Build.0 = Release|Any CPU + {789FFCD4-375D-4201-BCEA-776B3A8D2EAB}.Release|x86.ActiveCfg = Release|Any CPU + {789FFCD4-375D-4201-BCEA-776B3A8D2EAB}.Release|x86.Build.0 = Release|Any CPU {A4A7588A-0141-4D37-A614-4E889006CF1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A4A7588A-0141-4D37-A614-4E889006CF1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A4A7588A-0141-4D37-A614-4E889006CF1F}.Debug|x64.ActiveCfg = Debug|Any CPU + {A4A7588A-0141-4D37-A614-4E889006CF1F}.Debug|x64.Build.0 = Debug|Any CPU + {A4A7588A-0141-4D37-A614-4E889006CF1F}.Debug|x86.ActiveCfg = Debug|Any CPU + {A4A7588A-0141-4D37-A614-4E889006CF1F}.Debug|x86.Build.0 = Debug|Any CPU {A4A7588A-0141-4D37-A614-4E889006CF1F}.Release|Any CPU.ActiveCfg = Release|Any CPU {A4A7588A-0141-4D37-A614-4E889006CF1F}.Release|Any CPU.Build.0 = Release|Any CPU + {A4A7588A-0141-4D37-A614-4E889006CF1F}.Release|x64.ActiveCfg = Release|Any CPU + {A4A7588A-0141-4D37-A614-4E889006CF1F}.Release|x64.Build.0 = Release|Any CPU + {A4A7588A-0141-4D37-A614-4E889006CF1F}.Release|x86.ActiveCfg = Release|Any CPU + {A4A7588A-0141-4D37-A614-4E889006CF1F}.Release|x86.Build.0 = Release|Any CPU {DC21E7FB-E6D1-4EF9-8401-D0D4AA784DF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DC21E7FB-E6D1-4EF9-8401-D0D4AA784DF7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DC21E7FB-E6D1-4EF9-8401-D0D4AA784DF7}.Debug|x64.ActiveCfg = Debug|Any CPU + {DC21E7FB-E6D1-4EF9-8401-D0D4AA784DF7}.Debug|x64.Build.0 = Debug|Any CPU + {DC21E7FB-E6D1-4EF9-8401-D0D4AA784DF7}.Debug|x86.ActiveCfg = Debug|Any CPU + {DC21E7FB-E6D1-4EF9-8401-D0D4AA784DF7}.Debug|x86.Build.0 = Debug|Any CPU {DC21E7FB-E6D1-4EF9-8401-D0D4AA784DF7}.Release|Any CPU.ActiveCfg = Release|Any CPU {DC21E7FB-E6D1-4EF9-8401-D0D4AA784DF7}.Release|Any CPU.Build.0 = Release|Any CPU + {DC21E7FB-E6D1-4EF9-8401-D0D4AA784DF7}.Release|x64.ActiveCfg = Release|Any CPU + {DC21E7FB-E6D1-4EF9-8401-D0D4AA784DF7}.Release|x64.Build.0 = Release|Any CPU + {DC21E7FB-E6D1-4EF9-8401-D0D4AA784DF7}.Release|x86.ActiveCfg = Release|Any CPU + {DC21E7FB-E6D1-4EF9-8401-D0D4AA784DF7}.Release|x86.Build.0 = Release|Any CPU + {DD594970-E132-4712-9963-AC8979369DA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DD594970-E132-4712-9963-AC8979369DA0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DD594970-E132-4712-9963-AC8979369DA0}.Debug|x64.ActiveCfg = Debug|Any CPU + {DD594970-E132-4712-9963-AC8979369DA0}.Debug|x64.Build.0 = Debug|Any CPU + {DD594970-E132-4712-9963-AC8979369DA0}.Debug|x86.ActiveCfg = Debug|Any CPU + {DD594970-E132-4712-9963-AC8979369DA0}.Debug|x86.Build.0 = Debug|Any CPU + {DD594970-E132-4712-9963-AC8979369DA0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DD594970-E132-4712-9963-AC8979369DA0}.Release|Any CPU.Build.0 = Release|Any CPU + {DD594970-E132-4712-9963-AC8979369DA0}.Release|x64.ActiveCfg = Release|Any CPU + {DD594970-E132-4712-9963-AC8979369DA0}.Release|x64.Build.0 = Release|Any CPU + {DD594970-E132-4712-9963-AC8979369DA0}.Release|x86.ActiveCfg = Release|Any CPU + {DD594970-E132-4712-9963-AC8979369DA0}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -69,6 +143,7 @@ Global {F797D011-4BE7-435F-9F5F-0E52140B787A} = {1BBD068A-5F5B-49FE-B391-B762ECDB31C9} {789FFCD4-375D-4201-BCEA-776B3A8D2EAB} = {1BBD068A-5F5B-49FE-B391-B762ECDB31C9} {DC21E7FB-E6D1-4EF9-8401-D0D4AA784DF7} = {1BBD068A-5F5B-49FE-B391-B762ECDB31C9} + {DD594970-E132-4712-9963-AC8979369DA0} = {1BBD068A-5F5B-49FE-B391-B762ECDB31C9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2496CF15-57E0-4409-AD08-7A98882B0088} diff --git a/Pmad.Drawing/README.md b/Pmad.Drawing/README.md index 7177741..b418bc1 100644 --- a/Pmad.Drawing/README.md +++ b/Pmad.Drawing/README.md @@ -39,7 +39,7 @@ Render.ToPng("output.png", new Vector2D(800, 600), surface => ### Render to PDF ```csharp -Render.ToPdf("output.pdf", PaperSize.A4Landscape, surface => +Render.ToPdf("output.pdf", new Vector2D(800, 600), surface => { // same drawing calls });