protected void ProduceResponse(HttpContext context, ITypeAccepter accepter, string title, RenderContext ctx, Size tileSize, AbstractMatrix transform, bool transparent = false, IDictionary <string, object> queryDefaults = null) { // New-style Options #region URL Parameters // TODO: move to ParseOptions (maybe - requires options to be parsed after stylesheet creation?) if (GetBoolOption("sscoords", queryDefaults: queryDefaults, defaultValue: false)) { ctx.Styles.hexCoordinateStyle = HexCoordinateStyle.Subsector; } if (GetBoolOption("allhexes", queryDefaults: queryDefaults, defaultValue: false)) { ctx.Styles.numberAllHexes = true; } if (GetBoolOption("nogrid", queryDefaults: queryDefaults, defaultValue: false)) { ctx.Styles.parsecGrid.visible = false; } if (!GetBoolOption("routes", queryDefaults: queryDefaults, defaultValue: true)) { ctx.Styles.macroRoutes.visible = false; ctx.Styles.microRoutes.visible = false; } if (!GetBoolOption("rifts", queryDefaults: queryDefaults, defaultValue: true)) { ctx.Styles.showRiftOverlay = false; } if (GetBoolOption("po", queryDefaults: queryDefaults, defaultValue: false)) { ctx.Styles.populationOverlay.visible = true; } if (GetBoolOption("im", queryDefaults: queryDefaults, defaultValue: false)) { ctx.Styles.importanceOverlay.visible = true; } if (GetBoolOption("cp", queryDefaults: queryDefaults, defaultValue: false)) { ctx.Styles.capitalOverlay.visible = true; } if (GetBoolOption("stellar", queryDefaults: queryDefaults, defaultValue: false)) { ctx.Styles.showStellarOverlay = true; } ctx.Styles.dimUnofficialSectors = GetBoolOption("dimunofficial", queryDefaults: queryDefaults, defaultValue: false); ctx.Styles.colorCodeSectorStatus = GetBoolOption("review", queryDefaults: queryDefaults, defaultValue: false); ctx.Styles.droyneWorlds.visible = GetBoolOption("dw", queryDefaults: queryDefaults, defaultValue: false); ctx.Styles.minorHomeWorlds.visible = GetBoolOption("mh", queryDefaults: queryDefaults, defaultValue: false); ctx.Styles.ancientsWorlds.visible = GetBoolOption("an", queryDefaults: queryDefaults, defaultValue: false); // TODO: Return an error if pattern is invalid? ctx.Styles.highlightWorldsPattern = HighlightWorldPattern.Parse( GetStringOption("hw", queryDefaults: queryDefaults, defaultValue: String.Empty).Replace(' ', '+')); ctx.Styles.highlightWorlds.visible = ctx.Styles.highlightWorldsPattern != null; double devicePixelRatio = GetDoubleOption("dpr", defaultValue: 1, queryDefaults: queryDefaults); devicePixelRatio = Math.Round(devicePixelRatio, 1); if (devicePixelRatio <= 0) { devicePixelRatio = 1; } if (devicePixelRatio > 2) { devicePixelRatio = 2; } bool dataURI = GetBoolOption("datauri", queryDefaults: queryDefaults, defaultValue: false); if (GetStringOption("milieu", SectorMap.DEFAULT_MILIEU) != SectorMap.DEFAULT_MILIEU) { // TODO: Make this declarative in resource files. if (ctx.Styles.macroBorders.visible) { ctx.Styles.macroBorders.visible = false; ctx.Styles.microBorders.visible = true; } ctx.Styles.macroNames.visible = false; ctx.Styles.macroRoutes.visible = false; } #endregion MemoryStream ms = null; if (dataURI) { ms = new MemoryStream(); } Stream outputStream = ms ?? Context.Response.OutputStream; if (accepter.Accepts(context, ContentTypes.Image.Svg, ignoreHeaderFallbacks: true)) { #region SVG Generation using (var svg = new SVGGraphics(tileSize.Width, tileSize.Height)) { RenderToGraphics(ctx, transform, svg); using (var stream = new MemoryStream()) { svg.Serialize(new StreamWriter(stream)); context.Response.ContentType = ContentTypes.Image.Svg; if (!dataURI) { context.Response.AddHeader("content-length", stream.Length.ToString()); context.Response.AddHeader("content-disposition", $"inline;filename=\"{Util.SanitizeFilename(title)}.svg\""); } stream.WriteTo(outputStream); } } #endregion } else if (accepter.Accepts(context, ContentTypes.Application.Pdf, ignoreHeaderFallbacks: true)) { #region PDF Generation using (var document = new PdfDocument()) { document.Version = 14; // 1.4 for opacity document.Info.Title = title; document.Info.Author = "Joshua Bell"; document.Info.Creator = "TravellerMap.com"; document.Info.Subject = DateTime.Now.ToString("F", CultureInfo.InvariantCulture); document.Info.Keywords = "The Traveller game in all forms is owned by Far Future Enterprises. Copyright (C) 1977 - 2019 Far Future Enterprises. Traveller is a registered trademark of Far Future Enterprises."; // TODO: Credits/Copyright // This is close, but doesn't define the namespace correctly: // document.Info.Elements.Add( new KeyValuePair<string, PdfItem>( "/photoshop/Copyright", new PdfString( "HelloWorld" ) ) ); PdfPage page = document.AddPage(); // NOTE: only PageUnit currently supported in MGraphics is Points page.Width = XUnit.FromPoint(tileSize.Width); page.Height = XUnit.FromPoint(tileSize.Height); using (var gfx = new PdfSharpGraphics(XGraphics.FromPdfPage(page))) { RenderToGraphics(ctx, transform, gfx); using (var stream = new MemoryStream()) { document.Save(stream, closeStream: false); context.Response.ContentType = ContentTypes.Application.Pdf; if (!dataURI) { context.Response.AddHeader("content-length", stream.Length.ToString()); context.Response.AddHeader("content-disposition", $"inline;filename=\"{Util.SanitizeFilename(title)}.pdf\""); } stream.WriteTo(outputStream); } } } #endregion } else { #region Bitmap Generation int width = (int)Math.Floor(tileSize.Width * devicePixelRatio); int height = (int)Math.Floor(tileSize.Height * devicePixelRatio); using (var bitmap = TryConstructBitmap(width, height, PixelFormat.Format32bppArgb)) { if (bitmap == null) { throw new HttpError(500, "Internal Server Error", $"Failed to allocate bitmap ({width}x{height}). Insufficient memory?"); } if (transparent) { bitmap.MakeTransparent(); } using (var g = System.Drawing.Graphics.FromImage(bitmap)) { g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; using (var graphics = new BitmapGraphics(g)) { graphics.ScaleTransform((float)devicePixelRatio); RenderToGraphics(ctx, transform, graphics); } } BitmapResponse(context.Response, outputStream, ctx.Styles, bitmap, transparent ? ContentTypes.Image.Png : null); } #endregion } if (dataURI) { string contentType = context.Response.ContentType; context.Response.ContentType = ContentTypes.Text.Plain; ms.Seek(0, SeekOrigin.Begin); context.Response.Output.Write("data:"); context.Response.Output.Write(contentType); context.Response.Output.Write(";base64,"); context.Response.Output.Flush(); System.Security.Cryptography.ICryptoTransform encoder = new System.Security.Cryptography.ToBase64Transform(); using (System.Security.Cryptography.CryptoStream cs = new System.Security.Cryptography.CryptoStream(context.Response.OutputStream, encoder, System.Security.Cryptography.CryptoStreamMode.Write)) { ms.WriteTo(cs); cs.FlushFinalBlock(); } } context.Response.Flush(); context.Response.Close(); return; }