Mac: Add printing support

This commit is contained in:
Ben Olden-Cooligan 2023-03-26 17:45:04 -07:00
parent 016339cb2a
commit 985ac260a2
6 changed files with 146 additions and 13 deletions

View File

@ -78,6 +78,11 @@ public class MacImageContext : ImageContext
public override ITiffWriter TiffWriter { get; } = new MacTiffWriter();
public NSImage RenderToNsImage(IRenderableImage image)
{
return ((MacImage) Render(image)).NsImage;
}
private IMemoryImage CreateImage(NSImageRep rep)
{
NSImage frame;

View File

@ -0,0 +1,18 @@
namespace NAPS2.Images.Mac;
public static class MacImageExtensions
{
public static NSImage RenderToNsImage(this IRenderableImage image)
{
var macImageContext = image.ImageContext as MacImageContext ??
throw new ArgumentException("The provided image does not have a MacImageContext");
return macImageContext.RenderToNsImage(image);
}
public static NSImage AsNsImage(this IMemoryImage image)
{
var macImage = image as MacImage ?? throw new ArgumentException("Expected a MacImage", nameof(image));
return macImage.NsImage;
}
}

View File

@ -81,11 +81,11 @@ public class MacDesktopForm : DesktopForm
new SeparatorMenuItem(),
Commands.SaveAll,
Commands.SaveSelected,
// TODO: Implement print/email on Mac/Linux
// new SeparatorMenuItem(),
new SeparatorMenuItem(),
// TODO: Implement email on Mac
// Commands.EmailAll,
// Commands.EmailSelected,
// Commands.Print,
Commands.Print,
new SeparatorMenuItem(),
Commands.PdfSettings,
Commands.ImageSettings,

View File

@ -0,0 +1,118 @@
using NAPS2.Images.Mac;
namespace NAPS2.ImportExport;
public class MacScannedImagePrinter : IScannedImagePrinter
{
public async Task<bool> PromptToPrint(
Eto.Forms.Window parentWindow, IList<ProcessedImage> images, IList<ProcessedImage> selectedImages)
{
if (!images.Any())
{
return false;
}
return await Task.Run(() =>
{
return Invoker.Current.InvokeGet(() =>
{
using var view = new PaginatedImageView(images);
var printOp = NSPrintOperation.FromView(view, new NSPrintInfo
{
LeftMargin = 0,
BottomMargin = 0,
RightMargin = 0,
TopMargin = 0,
HorizontalPagination = NSPrintingPaginationMode.Fit,
VerticalPagination = NSPrintingPaginationMode.Fit,
HorizontallyCentered = true,
VerticallyCentered = true,
Orientation = view.CurrentImage!.Width > view.CurrentImage.Height
? NSPrintingOrientation.Landscape
: NSPrintingOrientation.Portrait
});
if (printOp.RunOperation())
{
Log.Event(EventType.Print, new EventParams
{
Name = MiscResources.Print,
Pages = (int) printOp.PageRange.Length
});
return true;
}
return false;
});
});
}
private class PaginatedImageView : NSBox
{
private readonly IList<ProcessedImage> _images;
private int _pageToRender;
private int _lastRenderedPage = -1;
public PaginatedImageView(IList<ProcessedImage> images)
{
_images = images;
BorderType = NSBorderType.NoBorder;
TitlePosition = NSTitlePosition.NoTitle;
LoadImage();
}
public IMemoryImage? CurrentImage { get; private set; }
public override bool KnowsPageRange(ref NSRange range)
{
range = new NSRange(1, _images.Count);
return true;
}
public override void BeginPage(CGRect rect, CGPoint placement)
{
bool loaded = LoadImage();
if (loaded && Math.Sign(CurrentImage!.Width - CurrentImage.Height) != Math.Sign(Frame.Width - Frame.Height))
{
// Flip portrait/landscape to match output
var isOriginalPortrait = CurrentImage!.Width < CurrentImage.Height;
var angle = isOriginalPortrait ? -90 : 90;
CurrentImage = CurrentImage.PerformTransform(new RotationTransform(angle));
}
ContentView = new NSImageView
{
Image = CurrentImage!.AsNsImage(),
ImageAlignment = NSImageAlignment.Center,
ImageScaling = NSImageScale.ProportionallyUpOrDown
};
base.BeginPage(rect, placement);
}
public override CGRect RectForPage(nint pageNumber)
{
_pageToRender = (int) pageNumber - 1;
var operation = NSPrintOperation.CurrentOperation;
if (Frame.Size != operation.PrintInfo.PaperSize)
{
SetFrameSize(operation.PrintInfo.PaperSize);
}
return new CGRect(new CGPoint(0, 0), operation.PrintInfo.PaperSize);
}
private bool LoadImage()
{
if (_lastRenderedPage == _pageToRender) return false;
_lastRenderedPage = _pageToRender;
CurrentImage?.Dispose();
CurrentImage = _images[_pageToRender].Render();
return true;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
CurrentImage?.Dispose();
}
base.Dispose(disposing);
}
}
}

View File

@ -16,7 +16,7 @@ public class MacModule : GuiModule
base.Load(builder);
builder.RegisterType<StubNotificationManager>().As<INotificationManager>().SingleInstance();
builder.RegisterType<StubScannedImagePrinter>().As<IScannedImagePrinter>();
builder.RegisterType<MacScannedImagePrinter>().As<IScannedImagePrinter>();
builder.RegisterType<MacDarkModeProvider>().As<IDarkModeProvider>();
builder.RegisterType<MacImageContext>().As<ImageContext>();
builder.RegisterType<MacImageContext>().AsSelf();
@ -29,14 +29,6 @@ public class MacModule : GuiModule
}
}
public class StubScannedImagePrinter : IScannedImagePrinter
{
public Task<bool> PromptToPrint(IList<ProcessedImage> images, IList<ProcessedImage> selectedImages)
{
return Task.FromResult(false);
}
}
public class StubNotificationManager : INotificationManager
{
public void PdfSaved(string path)

View File

@ -20,7 +20,7 @@ public class MacSystemCompat : ISystemCompat
public bool CanEmail => false;
public bool CanPrint => false;
public bool CanPrint => true;
public bool ShouldRememberBackgroundOperations => false;