/// <summary> /// Encode output image as PDF /// </summary> /// <param name="surface"></param> /// <param name="width">Requested output width (pixels)</param> /// <param name="height">Requested output height (pixels)</param> /// <param name="q">Image quality (percentage)</param> /// <param name="pdfMetadata">Optional metadata to include in the PDF</param> /// <returns></returns> public static Stream EncodePdf(SKSurface surface, int width, int height, int q, Conf.PdfMetadata pdfMetadata) { // have to encode to JPEG then paint the encoded bytes, otherwise you get full JP2 quality var output = new MemoryStream(); var metadata = new SKDocumentPdfMetadata() { Creation = DateTime.Now, }; if (null != pdfMetadata) { metadata.Author = pdfMetadata.Author; } using (var skstream = new SKManagedWStream(output)) using (var writer = SKDocument.CreatePdf(skstream, metadata)) using (var snapshot = surface.Snapshot()) using (var data = snapshot.Encode(SKEncodedImageFormat.Jpeg, q)) using (var image = SKImage.FromEncodedData(data)) using (var paint = new SKPaint()) { using (var canvas = writer.BeginPage(width, height)) { paint.FilterQuality = SKFilterQuality.High; canvas.DrawImage(image, new SKRect(0, 0, width, height), paint); writer.EndPage(); } } output.Seek(0, SeekOrigin.Begin); return(output); }
/// <summary> /// Process image pipeline /// <para>Region THEN Size THEN Rotation THEN Quality THEN Format</para> /// </summary> /// <param name="imageUri">The <see cref="Uri"/> of the source image</param> /// <param name="request">The parsed and validated IIIF Image API request</param> /// <param name="quality">Image output encoding quality settings</param> /// <param name="allowSizeAboveFull">Allow output image dimensions to exceed that of the source image</param> /// <param name="pdfMetadata">Optional PDF metadata fields</param> /// <returns></returns> public async Task <Stream> ProcessImage(Uri imageUri, ImageRequest request, Conf.ImageQuality quality, bool allowSizeAboveFull, Conf.PdfMetadata pdfMetadata) { var encodingStrategy = GetEncodingStrategy(request.Format); if (encodingStrategy == EncodingStrategy.Unknown) { throw new ArgumentException("Unsupported format", "format"); } var loader = new ImageLoader { HttpClient = HttpClient, Log = Log }; (var state, var imageRegion) = await loader.ExtractRegion(imageUri, request, allowSizeAboveFull, quality); using (imageRegion) { var expectedWidth = state.OutputWidth; var expectedHeight = state.OutputHeight; var alphaType = request.Quality == ImageQuality.bitonal ? SKAlphaType.Opaque : SKAlphaType.Premul; (var angle, var originX, var originY, var newImgWidth, var newImgHeight) = Rotate(expectedWidth, expectedHeight, request.Rotation.Degrees); using (var surface = SKSurface.Create(width: newImgWidth, height: newImgHeight, colorType: SKImageInfo.PlatformColorType, alphaType: alphaType)) using (var canvas = surface.Canvas) using (var region = new SKRegion()) { // If the rotation parameter includes mirroring ("!"), the mirroring is applied before the rotation. if (request.Rotation.Mirror) { canvas.Translate(newImgWidth, 0); canvas.Scale(-1, 1); } canvas.Translate(originX, originY); canvas.RotateDegrees(angle, 0, 0); // reset clip rects to rotated boundaries region.SetRect(new SKRectI(0 - (int)originX, 0 - (int)originY, newImgWidth, newImgHeight)); canvas.ClipRegion(region); // quality if (request.Quality == ImageQuality.gray || request.Quality == ImageQuality.bitonal) { var contrast = request.Quality == ImageQuality.gray ? 0.1f : 1f; using (var cf = SKColorFilter.CreateHighContrast(true, SKHighContrastConfigInvertStyle.NoInvert, contrast)) using (var paint = new SKPaint()) { paint.FilterQuality = SKFilterQuality.High; paint.ColorFilter = cf; canvas.DrawImage(imageRegion, new SKRect(0, 0, expectedWidth, expectedHeight), paint); } } else { using (var paint = new SKPaint()) { paint.FilterQuality = SKFilterQuality.High; canvas.DrawImage(imageRegion, new SKRect(0, 0, expectedWidth, expectedHeight), paint); } } return(Encode(surface, expectedWidth, expectedHeight, encodingStrategy, request.Format, quality.GetOutputFormatQuality(request.Format), pdfMetadata, state.HorizontalResolution, state.VerticalResolution)); } } }
private static Stream Encode(SKSurface surface, int width, int height, EncodingStrategy encodingStrategy, ImageFormat format, int q, Conf.PdfMetadata pdfMetadata, ushort horizontalResolution, ushort verticalResolution) { switch (encodingStrategy) { case EncodingStrategy.Skia: FormatLookup.TryGetValue(format, out SKEncodedImageFormat formatType); return(EncodeSkiaImage(surface, formatType, q, horizontalResolution, verticalResolution)); case EncodingStrategy.PDF: return(EncodePdf(surface, width, height, q, pdfMetadata)); case EncodingStrategy.JPEG2000: return(Jpeg2000.Compressor.Compress(surface.Snapshot())); case EncodingStrategy.Tifflib: return(Image.Tiff.TiffEncoder.Encode(surface.Snapshot())); default: throw new ArgumentException("Unsupported format", "format"); } }