More work on the modularization of the file format handlers, trying to more cleanly separate the code for the coming next steps.

This commit is contained in:
Robin Krom 2022-02-20 12:47:42 +01:00
commit aa5379cbd6
No known key found for this signature in database
GPG key ID: BCC01364F1371490
23 changed files with 284 additions and 282 deletions

View file

@ -332,7 +332,7 @@ EndSelection:<<<<<<<4
if (IsValidStream(imageStream)) if (IsValidStream(imageStream))
{ {
// TODO: How to check if we support "just a stream"? // TODO: How to check if we support "just a stream"?
using (ImageHelper.FromStream(imageStream)) using (ImageIO.FromStream(imageStream))
{ {
// If we get here, there is an image // If we get here, there is an image
return true; return true;
@ -967,7 +967,7 @@ EndSelection:<<<<<<<4
{ {
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false); SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false);
// Create the image which is going to be saved so we don't create it multiple times // Create the image which is going to be saved so we don't create it multiple times
disposeImage = ImageOutput.CreateImageFromSurface(surface, outputSettings, out imageToSave); disposeImage = ImageIO.CreateImageFromSurface(surface, outputSettings, out imageToSave);
try try
{ {
// Create PNG stream // Create PNG stream
@ -976,7 +976,7 @@ EndSelection:<<<<<<<4
pngStream = new MemoryStream(); pngStream = new MemoryStream();
// PNG works for e.g. Powerpoint // PNG works for e.g. Powerpoint
SurfaceOutputSettings pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false); SurfaceOutputSettings pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false);
ImageOutput.SaveToStream(imageToSave, null, pngStream, pngOutputSettings); ImageIO.SaveToStream(imageToSave, null, pngStream, pngOutputSettings);
pngStream.Seek(0, SeekOrigin.Begin); pngStream.Seek(0, SeekOrigin.Begin);
// Set the PNG stream // Set the PNG stream
dataObject.SetData(FORMAT_PNG, false, pngStream); dataObject.SetData(FORMAT_PNG, false, pngStream);
@ -1058,7 +1058,7 @@ EndSelection:<<<<<<<4
// Set the HTML // Set the HTML
if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.HTML)) if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.HTML))
{ {
string tmpFile = ImageOutput.SaveToTmpFile(surface, new SurfaceOutputSettings(OutputFormat.png, 100, false), null); string tmpFile = ImageIO.SaveToTmpFile(surface, new SurfaceOutputSettings(OutputFormat.png, 100, false), null);
string html = GetHtmlString(surface, tmpFile); string html = GetHtmlString(surface, tmpFile);
dataObject.SetText(html, TextDataFormat.Html); dataObject.SetText(html, TextDataFormat.Html);
} }
@ -1076,11 +1076,11 @@ EndSelection:<<<<<<<4
// Check if we can use the previously used image // Check if we can use the previously used image
if (imageToSave.PixelFormat != PixelFormat.Format8bppIndexed) if (imageToSave.PixelFormat != PixelFormat.Format8bppIndexed)
{ {
ImageOutput.SaveToStream(imageToSave, surface, tmpPngStream, pngOutputSettings); ImageIO.SaveToStream(imageToSave, surface, tmpPngStream, pngOutputSettings);
} }
else else
{ {
ImageOutput.SaveToStream(surface, tmpPngStream, pngOutputSettings); ImageIO.SaveToStream(surface, tmpPngStream, pngOutputSettings);
} }
html = GetHtmlDataUrlString(surface, tmpPngStream); html = GetHtmlDataUrlString(surface, tmpPngStream);

View file

@ -24,16 +24,12 @@ using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using Greenshot.Base.Core.Enums; using Greenshot.Base.Core.Enums;
using Greenshot.Base.Core.FileFormatHandlers;
using Greenshot.Base.Effects; using Greenshot.Base.Effects;
using Greenshot.Base.IniFile; using Greenshot.Base.IniFile;
using Greenshot.Base.Interfaces;
using Greenshot.Base.UnmanagedHelpers; using Greenshot.Base.UnmanagedHelpers;
using log4net; using log4net;
using Brush = System.Drawing.Brush; using Brush = System.Drawing.Brush;
@ -294,127 +290,6 @@ namespace Greenshot.Base.Core
return cropRectangle; return cropRectangle;
} }
/// <summary>
/// Load an image from file
/// </summary>
/// <param name="filename"></param>
/// <returns></returns>
public static Image LoadImage(string filename)
{
if (string.IsNullOrEmpty(filename))
{
return null;
}
if (!File.Exists(filename))
{
return null;
}
Image fileImage;
Log.InfoFormat("Loading image from file {0}", filename);
// Fixed lock problem Bug #3431881
using (Stream imageFileStream = File.OpenRead(filename))
{
fileImage = FromStream(imageFileStream, Path.GetExtension(filename));
}
if (fileImage != null)
{
Log.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", filename, fileImage.Width, fileImage.Height, fileImage.PixelFormat,
fileImage.HorizontalResolution, fileImage.VerticalResolution);
}
return fileImage;
}
/// <summary>
/// Based on: https://www.codeproject.com/KB/cs/IconExtractor.aspx
/// And a hint from: https://www.codeproject.com/KB/cs/IconLib.aspx
/// </summary>
/// <param name="iconStream">Stream with the icon information</param>
/// <returns>Bitmap with the Vista Icon (256x256)</returns>
private static Bitmap ExtractVistaIcon(Stream iconStream)
{
const int sizeIconDir = 6;
const int sizeIconDirEntry = 16;
Bitmap bmpPngExtracted = null;
try
{
byte[] srcBuf = new byte[iconStream.Length];
iconStream.Read(srcBuf, 0, (int) iconStream.Length);
int iCount = BitConverter.ToInt16(srcBuf, 4);
for (int iIndex = 0; iIndex < iCount; iIndex++)
{
int iWidth = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex];
int iHeight = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex + 1];
if (iWidth == 0 && iHeight == 0)
{
int iImageSize = BitConverter.ToInt32(srcBuf, sizeIconDir + sizeIconDirEntry * iIndex + 8);
int iImageOffset = BitConverter.ToInt32(srcBuf, sizeIconDir + sizeIconDirEntry * iIndex + 12);
using MemoryStream destStream = new MemoryStream();
destStream.Write(srcBuf, iImageOffset, iImageSize);
destStream.Seek(0, SeekOrigin.Begin);
bmpPngExtracted = new Bitmap(destStream); // This is PNG! :)
break;
}
}
}
catch
{
return null;
}
return bmpPngExtracted;
}
/// <summary>
/// See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms648069%28v=vs.85%29.aspx
/// </summary>
/// <param name="location">The file (EXE or DLL) to get the icon from</param>
/// <param name="index">Index of the icon</param>
/// <param name="takeLarge">true if the large icon is wanted</param>
/// <returns>Icon</returns>
public static Icon ExtractAssociatedIcon(string location, int index, bool takeLarge)
{
Shell32.ExtractIconEx(location, index, out var large, out var small, 1);
Icon returnIcon = null;
bool isLarge = false;
bool isSmall = false;
try
{
if (takeLarge && !IntPtr.Zero.Equals(large))
{
returnIcon = Icon.FromHandle(large);
isLarge = true;
}
else if (!IntPtr.Zero.Equals(small))
{
returnIcon = Icon.FromHandle(small);
isSmall = true;
}
else if (!IntPtr.Zero.Equals(large))
{
returnIcon = Icon.FromHandle(large);
isLarge = true;
}
}
finally
{
if (isLarge && !IntPtr.Zero.Equals(small))
{
User32.DestroyIcon(small);
}
if (isSmall && !IntPtr.Zero.Equals(large))
{
User32.DestroyIcon(large);
}
}
return returnIcon;
}
/// <summary> /// <summary>
/// Apply the effect to the bitmap /// Apply the effect to the bitmap
/// </summary> /// </summary>
@ -1568,7 +1443,7 @@ namespace Greenshot.Base.Core
nPercentW = nPercentH; nPercentW = nPercentH;
if (canvasUseNewSize) if (canvasUseNewSize)
{ {
destX = Math.Max(0, System.Convert.ToInt32((newWidth - sourceImage.Width * nPercentW) / 2)); destX = Math.Max(0, Convert.ToInt32((newWidth - sourceImage.Width * nPercentW) / 2));
} }
} }
else if ((int) nPercentH == 1) else if ((int) nPercentH == 1)
@ -1576,7 +1451,7 @@ namespace Greenshot.Base.Core
nPercentH = nPercentW; nPercentH = nPercentW;
if (canvasUseNewSize) if (canvasUseNewSize)
{ {
destY = Math.Max(0, System.Convert.ToInt32((newHeight - sourceImage.Height * nPercentH) / 2)); destY = Math.Max(0, Convert.ToInt32((newHeight - sourceImage.Height * nPercentH) / 2));
} }
} }
else if ((int) nPercentH != 0 && nPercentH < nPercentW) else if ((int) nPercentH != 0 && nPercentH < nPercentW)
@ -1584,7 +1459,7 @@ namespace Greenshot.Base.Core
nPercentW = nPercentH; nPercentW = nPercentH;
if (canvasUseNewSize) if (canvasUseNewSize)
{ {
destX = Math.Max(0, System.Convert.ToInt32((newWidth - sourceImage.Width * nPercentW) / 2)); destX = Math.Max(0, Convert.ToInt32((newWidth - sourceImage.Width * nPercentW) / 2));
} }
} }
else else
@ -1592,7 +1467,7 @@ namespace Greenshot.Base.Core
nPercentH = nPercentW; nPercentH = nPercentW;
if (canvasUseNewSize) if (canvasUseNewSize)
{ {
destY = Math.Max(0, System.Convert.ToInt32((newHeight - sourceImage.Height * nPercentH) / 2)); destY = Math.Max(0, Convert.ToInt32((newHeight - sourceImage.Height * nPercentH) / 2));
} }
} }
} }
@ -1631,102 +1506,7 @@ namespace Greenshot.Base.Core
return newImage; return newImage;
} }
/// <summary>
/// Load a Greenshot surface from a stream
/// </summary>
/// <param name="surfaceFileStream">Stream</param>
/// <param name="returnSurface"></param>
/// <returns>ISurface</returns>
public static ISurface LoadGreenshotSurface(Stream surfaceFileStream, ISurface returnSurface)
{
Image fileImage;
// Fixed problem that the bitmap stream is disposed... by Cloning the image
// This also ensures the bitmap is correctly created
// We create a copy of the bitmap, so everything else can be disposed
surfaceFileStream.Position = 0;
using (Image tmpImage = Image.FromStream(surfaceFileStream, true, true))
{
Log.DebugFormat("Loaded .greenshot file with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat);
fileImage = Clone(tmpImage);
}
// Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor)
const int markerSize = 14;
surfaceFileStream.Seek(-markerSize, SeekOrigin.End);
using (StreamReader streamReader = new StreamReader(surfaceFileStream))
{
var greenshotMarker = streamReader.ReadToEnd();
if (!greenshotMarker.StartsWith("Greenshot"))
{
throw new ArgumentException("Stream is not a Greenshot file!");
}
Log.InfoFormat("Greenshot file format: {0}", greenshotMarker);
const int filesizeLocation = 8 + markerSize;
surfaceFileStream.Seek(-filesizeLocation, SeekOrigin.End);
using BinaryReader reader = new BinaryReader(surfaceFileStream);
long bytesWritten = reader.ReadInt64();
surfaceFileStream.Seek(-(bytesWritten + filesizeLocation), SeekOrigin.End);
returnSurface.LoadElementsFromStream(surfaceFileStream);
}
if (fileImage != null)
{
returnSurface.Image = fileImage;
Log.InfoFormat("Information about .greenshot file: {0}x{1}-{2} Resolution {3}x{4}", fileImage.Width, fileImage.Height, fileImage.PixelFormat,
fileImage.HorizontalResolution, fileImage.VerticalResolution);
}
return returnSurface;
}
/// <summary>
/// Create an image from a stream, if an extension is supplied more formats are supported.
/// </summary>
/// <param name="stream">Stream</param>
/// <param name="extension"></param>
/// <returns>Image</returns>
public static Image FromStream(Stream stream, string extension = null)
{
if (stream == null)
{
return null;
}
if (!string.IsNullOrEmpty(extension))
{
extension = extension.Replace(".", string.Empty);
}
var startingPosition = stream.Position;
// Make sure we can try multiple times
if (!stream.CanSeek)
{
var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
stream = memoryStream;
// As we are if a different stream, which starts at 0, change the starting position
startingPosition = 0;
}
var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances<IFileFormatHandler>();
foreach (var fileFormatHandler in fileFormatHandlers
.Where(ffh => ffh.Supports(FileFormatHandlerActions.LoadFromStream, extension))
.OrderBy(ffh => ffh.PriorityFor(FileFormatHandlerActions.LoadFromStream, extension)))
{
stream.Seek(startingPosition, SeekOrigin.Begin);
if (fileFormatHandler.TryLoadFromStream(stream, extension, out var bitmap))
{
return bitmap;
}
}
return null;
}
/// <summary> /// <summary>
/// Rotate the image /// Rotate the image
/// </summary> /// </summary>

View file

@ -24,6 +24,7 @@ using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@ -35,6 +36,7 @@ using Greenshot.Base.Core.FileFormatHandlers;
using Greenshot.Base.IniFile; using Greenshot.Base.IniFile;
using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Plugin; using Greenshot.Base.Interfaces.Plugin;
using Greenshot.Base.UnmanagedHelpers;
using log4net; using log4net;
namespace Greenshot.Base.Core namespace Greenshot.Base.Core
@ -42,9 +44,9 @@ namespace Greenshot.Base.Core
/// <summary> /// <summary>
/// Description of ImageOutput. /// Description of ImageOutput.
/// </summary> /// </summary>
public static class ImageOutput public static class ImageIO
{ {
private static readonly ILog Log = LogManager.GetLogger(typeof(ImageOutput)); private static readonly ILog Log = LogManager.GetLogger(typeof(ImageIO));
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>(); private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
private static readonly int PROPERTY_TAG_SOFTWARE_USED = 0x0131; private static readonly int PROPERTY_TAG_SOFTWARE_USED = 0x0131;
private static readonly Cache<string, string> TmpFileCache = new Cache<string, string>(10 * 60 * 60, RemoveExpiredTmpFile); private static readonly Cache<string, string> TmpFileCache = new Cache<string, string>(10 * 60 * 60, RemoveExpiredTmpFile);
@ -280,7 +282,7 @@ namespace Greenshot.Base.Core
// Fixed lock problem Bug #3431881 // Fixed lock problem Bug #3431881
using (Stream surfaceFileStream = File.OpenRead(fullPath)) using (Stream surfaceFileStream = File.OpenRead(fullPath))
{ {
returnSurface = ImageHelper.LoadGreenshotSurface(surfaceFileStream, returnSurface); returnSurface = LoadGreenshotSurface(surfaceFileStream, returnSurface);
} }
if (returnSurface != null) if (returnSurface != null)
@ -522,5 +524,220 @@ namespace Greenshot.Base.Core
File.Delete(path); File.Delete(path);
} }
} }
/// <summary>
/// Load an image from file
/// </summary>
/// <param name="filename"></param>
/// <returns></returns>
public static Image LoadImage(string filename)
{
if (string.IsNullOrEmpty(filename))
{
return null;
}
if (!File.Exists(filename))
{
return null;
}
Image fileImage;
Log.InfoFormat("Loading image from file {0}", filename);
// Fixed lock problem Bug #3431881
using (Stream imageFileStream = File.OpenRead(filename))
{
fileImage = FromStream(imageFileStream, Path.GetExtension(filename));
}
if (fileImage != null)
{
Log.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", filename, fileImage.Width, fileImage.Height, fileImage.PixelFormat,
fileImage.HorizontalResolution, fileImage.VerticalResolution);
}
return fileImage;
}
/// <summary>
/// Based on: https://www.codeproject.com/KB/cs/IconExtractor.aspx
/// And a hint from: https://www.codeproject.com/KB/cs/IconLib.aspx
/// </summary>
/// <param name="iconStream">Stream with the icon information</param>
/// <returns>Bitmap with the Vista Icon (256x256)</returns>
private static Bitmap ExtractVistaIcon(Stream iconStream)
{
const int sizeIconDir = 6;
const int sizeIconDirEntry = 16;
Bitmap bmpPngExtracted = null;
try
{
byte[] srcBuf = new byte[iconStream.Length];
iconStream.Read(srcBuf, 0, (int)iconStream.Length);
int iCount = BitConverter.ToInt16(srcBuf, 4);
for (int iIndex = 0; iIndex < iCount; iIndex++)
{
int iWidth = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex];
int iHeight = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex + 1];
if (iWidth == 0 && iHeight == 0)
{
int iImageSize = BitConverter.ToInt32(srcBuf, sizeIconDir + sizeIconDirEntry * iIndex + 8);
int iImageOffset = BitConverter.ToInt32(srcBuf, sizeIconDir + sizeIconDirEntry * iIndex + 12);
using MemoryStream destStream = new MemoryStream();
destStream.Write(srcBuf, iImageOffset, iImageSize);
destStream.Seek(0, SeekOrigin.Begin);
bmpPngExtracted = new Bitmap(destStream); // This is PNG! :)
break;
}
}
}
catch
{
return null;
}
return bmpPngExtracted;
}
/// <summary>
/// See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms648069%28v=vs.85%29.aspx
/// </summary>
/// <param name="location">The file (EXE or DLL) to get the icon from</param>
/// <param name="index">Index of the icon</param>
/// <param name="takeLarge">true if the large icon is wanted</param>
/// <returns>Icon</returns>
public static Icon ExtractAssociatedIcon(string location, int index, bool takeLarge)
{
Shell32.ExtractIconEx(location, index, out var large, out var small, 1);
Icon returnIcon = null;
bool isLarge = false;
bool isSmall = false;
try
{
if (takeLarge && !IntPtr.Zero.Equals(large))
{
returnIcon = Icon.FromHandle(large);
isLarge = true;
}
else if (!IntPtr.Zero.Equals(small))
{
returnIcon = Icon.FromHandle(small);
isSmall = true;
}
else if (!IntPtr.Zero.Equals(large))
{
returnIcon = Icon.FromHandle(large);
isLarge = true;
}
}
finally
{
if (isLarge && !IntPtr.Zero.Equals(small))
{
User32.DestroyIcon(small);
}
if (isSmall && !IntPtr.Zero.Equals(large))
{
User32.DestroyIcon(large);
}
}
return returnIcon;
}
/// <summary>
/// Create an image from a stream, if an extension is supplied more formats are supported.
/// </summary>
/// <param name="stream">Stream</param>
/// <param name="extension"></param>
/// <returns>Image</returns>
public static Image FromStream(Stream stream, string extension = null)
{
if (stream == null)
{
return null;
}
if (!string.IsNullOrEmpty(extension))
{
extension = extension.Replace(".", string.Empty);
}
var startingPosition = stream.Position;
// Make sure we can try multiple times
if (!stream.CanSeek)
{
var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
stream = memoryStream;
// As we are if a different stream, which starts at 0, change the starting position
startingPosition = 0;
}
var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances<IFileFormatHandler>();
foreach (var fileFormatHandler in fileFormatHandlers
.Where(ffh => ffh.Supports(FileFormatHandlerActions.LoadFromStream, extension))
.OrderBy(ffh => ffh.PriorityFor(FileFormatHandlerActions.LoadFromStream, extension)))
{
stream.Seek(startingPosition, SeekOrigin.Begin);
if (fileFormatHandler.TryLoadFromStream(stream, extension, out var bitmap))
{
return bitmap;
}
}
return null;
}
/// <summary>
/// Load a Greenshot surface from a stream
/// </summary>
/// <param name="surfaceFileStream">Stream</param>
/// <param name="returnSurface"></param>
/// <returns>ISurface</returns>
public static ISurface LoadGreenshotSurface(Stream surfaceFileStream, ISurface returnSurface)
{
Image fileImage;
// Fixed problem that the bitmap stream is disposed... by Cloning the image
// This also ensures the bitmap is correctly created
// We create a copy of the bitmap, so everything else can be disposed
surfaceFileStream.Position = 0;
using (Image tmpImage = Image.FromStream(surfaceFileStream, true, true))
{
Log.DebugFormat("Loaded .greenshot file with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat);
fileImage = ImageHelper.Clone(tmpImage);
}
// Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor)
const int markerSize = 14;
surfaceFileStream.Seek(-markerSize, SeekOrigin.End);
using (StreamReader streamReader = new StreamReader(surfaceFileStream))
{
var greenshotMarker = streamReader.ReadToEnd();
if (!greenshotMarker.StartsWith("Greenshot"))
{
throw new ArgumentException("Stream is not a Greenshot file!");
}
Log.InfoFormat("Greenshot file format: {0}", greenshotMarker);
const int filesizeLocation = 8 + markerSize;
surfaceFileStream.Seek(-filesizeLocation, SeekOrigin.End);
using BinaryReader reader = new BinaryReader(surfaceFileStream);
long bytesWritten = reader.ReadInt64();
surfaceFileStream.Seek(-(bytesWritten + filesizeLocation), SeekOrigin.End);
returnSurface.LoadElementsFromStream(surfaceFileStream);
}
if (fileImage != null)
{
returnSurface.Image = fileImage;
Log.InfoFormat("Information about .greenshot file: {0}x{1}-{2} Resolution {3}x{4}", fileImage.Width, fileImage.Height, fileImage.PixelFormat,
fileImage.HorizontalResolution, fileImage.VerticalResolution);
}
return returnSurface;
}
} }
} }

View file

@ -732,7 +732,7 @@ namespace Greenshot.Base.Core
public string ToBase64String(Base64FormattingOptions formattingOptions) public string ToBase64String(Base64FormattingOptions formattingOptions)
{ {
using MemoryStream stream = new MemoryStream(); using MemoryStream stream = new MemoryStream();
ImageOutput.SaveToStream(_surface, stream, _outputSettings); ImageIO.SaveToStream(_surface, stream, _outputSettings);
return Convert.ToBase64String(stream.GetBuffer(), 0, (int) stream.Length, formattingOptions); return Convert.ToBase64String(stream.GetBuffer(), 0, (int) stream.Length, formattingOptions);
} }
@ -744,7 +744,7 @@ namespace Greenshot.Base.Core
public byte[] ToByteArray() public byte[] ToByteArray()
{ {
using MemoryStream stream = new MemoryStream(); using MemoryStream stream = new MemoryStream();
ImageOutput.SaveToStream(_surface, stream, _outputSettings); ImageIO.SaveToStream(_surface, stream, _outputSettings);
return stream.ToArray(); return stream.ToArray();
} }
@ -760,7 +760,7 @@ namespace Greenshot.Base.Core
string header = $"--{boundary}\r\nContent-Disposition: form-data; name=\"{name}\"; filename=\"{Filename ?? name}\";\r\nContent-Type: {ContentType}\r\n\r\n"; string header = $"--{boundary}\r\nContent-Disposition: form-data; name=\"{name}\"; filename=\"{Filename ?? name}\";\r\nContent-Type: {ContentType}\r\n\r\n";
formDataStream.Write(Encoding.UTF8.GetBytes(header), 0, Encoding.UTF8.GetByteCount(header)); formDataStream.Write(Encoding.UTF8.GetBytes(header), 0, Encoding.UTF8.GetByteCount(header));
ImageOutput.SaveToStream(_surface, formDataStream, _outputSettings); ImageIO.SaveToStream(_surface, formDataStream, _outputSettings);
} }
/// <summary> /// <summary>
@ -770,7 +770,7 @@ namespace Greenshot.Base.Core
public void WriteToStream(Stream dataStream) public void WriteToStream(Stream dataStream)
{ {
// Write the file data directly to the Stream, rather than serializing it to a string. // Write the file data directly to the Stream, rather than serializing it to a string.
ImageOutput.SaveToStream(_surface, dataStream, _outputSettings); ImageIO.SaveToStream(_surface, dataStream, _outputSettings);
} }
/// <summary> /// <summary>

View file

@ -158,7 +158,7 @@ namespace Greenshot.Base.Core
try try
{ {
using (Icon appIcon = ImageHelper.ExtractAssociatedIcon(path, index, CoreConfig.UseLargeIcons)) using (Icon appIcon = ImageIO.ExtractAssociatedIcon(path, index, CoreConfig.UseLargeIcons))
{ {
if (appIcon != null) if (appIcon != null)
{ {

View file

@ -41,7 +41,7 @@ namespace Greenshot.Editor.Drawing
{ {
private static readonly ILog Log = LogManager.GetLogger(typeof(ImageContainer)); private static readonly ILog Log = LogManager.GetLogger(typeof(ImageContainer));
private Image image; private Image _image;
/// <summary> /// <summary>
/// This is the shadow version of the bitmap, rendered once to save performance /// This is the shadow version of the bitmap, rendered once to save performance
@ -108,8 +108,8 @@ namespace Greenshot.Editor.Drawing
} }
else else
{ {
Width = image.Width; Width = _image.Width;
Height = image.Height; Height = _image.Height;
if (_shadowBitmap != null) if (_shadowBitmap != null)
{ {
Left += _shadowOffset.X; Left += _shadowOffset.X;
@ -125,13 +125,13 @@ namespace Greenshot.Editor.Drawing
// Remove all current bitmaps // Remove all current bitmaps
DisposeImage(); DisposeImage();
DisposeShadow(); DisposeShadow();
image = ImageHelper.Clone(value); _image = ImageHelper.Clone(value);
bool shadow = GetFieldValueAsBool(FieldType.SHADOW); bool shadow = GetFieldValueAsBool(FieldType.SHADOW);
CheckShadow(shadow); CheckShadow(shadow);
if (!shadow) if (!shadow)
{ {
Width = image.Width; Width = _image.Width;
Height = image.Height; Height = _image.Height;
} }
else else
{ {
@ -141,7 +141,7 @@ namespace Greenshot.Editor.Drawing
Top -= _shadowOffset.Y; Top -= _shadowOffset.Y;
} }
} }
get { return image; } get { return _image; }
} }
/// <summary> /// <summary>
@ -163,8 +163,8 @@ namespace Greenshot.Editor.Drawing
private void DisposeImage() private void DisposeImage()
{ {
image?.Dispose(); _image?.Dispose();
image = null; _image = null;
} }
private void DisposeShadow() private void DisposeShadow()
@ -187,9 +187,9 @@ namespace Greenshot.Editor.Drawing
Log.DebugFormat("Rotating element with {0} degrees.", rotateAngle); Log.DebugFormat("Rotating element with {0} degrees.", rotateAngle);
DisposeShadow(); DisposeShadow();
using var tmpMatrix = new Matrix(); using var tmpMatrix = new Matrix();
using (image) using (_image)
{ {
image = ImageHelper.ApplyEffect(image, new RotateEffect(rotateAngle), tmpMatrix); _image = ImageHelper.ApplyEffect(_image, new RotateEffect(rotateAngle), tmpMatrix);
} }
} }
@ -209,7 +209,8 @@ namespace Greenshot.Editor.Drawing
// Always make sure ImageHelper.LoadBitmap results are disposed some time, // Always make sure ImageHelper.LoadBitmap results are disposed some time,
// as we close the bitmap internally, we need to do it afterwards // as we close the bitmap internally, we need to do it afterwards
using (var tmpImage = ImageHelper.LoadImage(filename)) // TODO: Replace with some other code, like the file format handler, or move it completely outside of this class
using (var tmpImage = ImageIO.LoadImage(filename))
{ {
Image = tmpImage; Image = tmpImage;
} }
@ -229,7 +230,7 @@ namespace Greenshot.Editor.Drawing
} }
using var matrix = new Matrix(); using var matrix = new Matrix();
_shadowBitmap = ImageHelper.ApplyEffect(image, new DropShadowEffect(), matrix); _shadowBitmap = ImageHelper.ApplyEffect(_image, new DropShadowEffect(), matrix);
} }
/// <summary> /// <summary>
@ -239,7 +240,7 @@ namespace Greenshot.Editor.Drawing
/// <param name="rm"></param> /// <param name="rm"></param>
public override void Draw(Graphics graphics, RenderMode rm) public override void Draw(Graphics graphics, RenderMode rm)
{ {
if (image == null) if (_image == null)
{ {
return; return;
} }
@ -257,12 +258,12 @@ namespace Greenshot.Editor.Drawing
} }
else else
{ {
graphics.DrawImage(image, Bounds); graphics.DrawImage(_image, Bounds);
} }
} }
public override bool HasDefaultSize => true; public override bool HasDefaultSize => true;
public override Size DefaultSize => image?.Size ?? new Size(32, 32); public override Size DefaultSize => _image?.Size ?? new Size(32, 32);
} }
} }

View file

@ -86,7 +86,8 @@ namespace Greenshot.Editor.FileFormatHandlers
bitmapStream.Write(fileHeaderBytes, 0, fileHeaderSize); bitmapStream.Write(fileHeaderBytes, 0, fileHeaderSize);
bitmapStream.Write(dibBuffer, 0, dibBuffer.Length); bitmapStream.Write(dibBuffer, 0, dibBuffer.Length);
bitmapStream.Seek(0, SeekOrigin.Begin); bitmapStream.Seek(0, SeekOrigin.Begin);
bitmap = ImageHelper.FromStream(bitmapStream) as Bitmap; // TODO: Replace with a FileFormatHandler
bitmap = ImageIO.FromStream(bitmapStream) as Bitmap;
return true; return true;
} }
Log.Info("Using special DIBV5 / Format17 format reader"); Log.Info("Using special DIBV5 / Format17 format reader");

View file

@ -54,8 +54,8 @@ namespace Greenshot.Plugin.Confluence
Uri confluenceIconUri = new Uri("/Greenshot.Plugin.Confluence;component/Images/Confluence.ico", UriKind.Relative); Uri confluenceIconUri = new Uri("/Greenshot.Plugin.Confluence;component/Images/Confluence.ico", UriKind.Relative);
using (Stream iconStream = Application.GetResourceStream(confluenceIconUri)?.Stream) using (Stream iconStream = Application.GetResourceStream(confluenceIconUri)?.Stream)
{ {
// TODO: Check what to do with the IImage // TODO: Replace with FileFormatHandler
ConfluenceIcon = ImageHelper.FromStream(iconStream); ConfluenceIcon = ImageIO.FromStream(iconStream);
} }
IsInitialized = true; IsInitialized = true;

View file

@ -77,7 +77,7 @@ namespace Greenshot.Plugin.ExternalCommand
} }
bool runInBackground = config.RunInbackground[_presetCommand]; bool runInBackground = config.RunInbackground[_presetCommand];
string fullPath = captureDetails.Filename ?? ImageOutput.SaveNamedTmpFile(surface, captureDetails, outputSettings); string fullPath = captureDetails.Filename ?? ImageIO.SaveNamedTmpFile(surface, captureDetails, outputSettings);
string output; string output;
string error; string error;

View file

@ -179,7 +179,7 @@ namespace Greenshot.Plugin.Imgur
{ {
using (var requestStream = webRequest.GetRequestStream()) using (var requestStream = webRequest.GetRequestStream())
{ {
ImageOutput.SaveToStream(surfaceToUpload, requestStream, outputSettings); ImageIO.SaveToStream(surfaceToUpload, requestStream, outputSettings);
} }
using WebResponse response = webRequest.GetResponse(); using WebResponse response = webRequest.GetResponse();
@ -265,7 +265,8 @@ namespace Greenshot.Plugin.Imgur
Stream responseStream = response.GetResponseStream(); Stream responseStream = response.GetResponseStream();
if (responseStream != null) if (responseStream != null)
{ {
imgurInfo.Image = ImageHelper.FromStream(responseStream); // TODO: Replace with some other code, like the file format handler
imgurInfo.Image = ImageIO.FromStream(responseStream);
} }
} }

View file

@ -89,7 +89,7 @@ namespace Greenshot.Plugin.Office.Destinations
string imageFile = captureDetails.Filename; string imageFile = captureDetails.Filename;
if (imageFile == null || surface.Modified || !Regex.IsMatch(imageFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) if (imageFile == null || surface.Modified || !Regex.IsMatch(imageFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$"))
{ {
imageFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); imageFile = ImageIO.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat());
createdFile = true; createdFile = true;
} }
@ -107,7 +107,7 @@ namespace Greenshot.Plugin.Office.Destinations
// Cleanup imageFile if we created it here, so less tmp-files are generated and left // Cleanup imageFile if we created it here, so less tmp-files are generated and left
if (createdFile) if (createdFile)
{ {
ImageOutput.DeleteNamedTmpFile(imageFile); ImageIO.DeleteNamedTmpFile(imageFile);
} }
return exportInformation; return exportInformation;

View file

@ -160,7 +160,7 @@ namespace Greenshot.Plugin.Office.Destinations
string tmpFile = captureDetails.Filename; string tmpFile = captureDetails.Filename;
if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$"))
{ {
tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); tmpFile = ImageIO.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat());
} }
else else
{ {

View file

@ -114,7 +114,7 @@ namespace Greenshot.Plugin.Office.Destinations
Size imageSize = Size.Empty; Size imageSize = Size.Empty;
if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$"))
{ {
tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); tmpFile = ImageIO.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat());
imageSize = surface.Image.Size; imageSize = surface.Image.Size;
} }

View file

@ -88,7 +88,7 @@ namespace Greenshot.Plugin.Office.Destinations
string tmpFile = captureDetails.Filename; string tmpFile = captureDetails.Filename;
if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$"))
{ {
tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); tmpFile = ImageIO.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat());
} }
if (_documentCaption != null) if (_documentCaption != null)

View file

@ -97,7 +97,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
using var pngStream = new MemoryStream(); using var pngStream = new MemoryStream();
var pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false); var pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false);
ImageOutput.SaveToStream(surfaceToUpload, pngStream, pngOutputSettings); ImageIO.SaveToStream(surfaceToUpload, pngStream, pngOutputSettings);
var base64String = Convert.ToBase64String(pngStream.GetBuffer()); var base64String = Convert.ToBase64String(pngStream.GetBuffer());
var imageXmlStr = string.Format(XmlImageContent, base64String, surfaceToUpload.Image.Width, surfaceToUpload.Image.Height); var imageXmlStr = string.Format(XmlImageContent, base64String, surfaceToUpload.Image.Width, surfaceToUpload.Image.Height);
var pageChangesXml = string.Format(XmlOutline, imageXmlStr, page.Id, OnenoteNamespace2010, page.Name); var pageChangesXml = string.Format(XmlOutline, imageXmlStr, page.Id, OnenoteNamespace2010, page.Name);

View file

@ -151,7 +151,7 @@ namespace Greenshot.Plugin.Win10.Destinations
outputSettings.PreventGreenshotFormat(); outputSettings.PreventGreenshotFormat();
// Create capture for export // Create capture for export
ImageOutput.SaveToStream(surface, imageStream, outputSettings); ImageIO.SaveToStream(surface, imageStream, outputSettings);
imageStream.Position = 0; imageStream.Position = 0;
Log.Debug("Created RandomAccessStreamReference for the image"); Log.Debug("Created RandomAccessStreamReference for the image");
var imageRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(imageStream); var imageRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(imageStream);
@ -161,7 +161,7 @@ namespace Greenshot.Plugin.Win10.Destinations
using (var tmpImageForThumbnail = surface.GetImageForExport()) using (var tmpImageForThumbnail = surface.GetImageForExport())
using (var thumbnail = ImageHelper.CreateThumbnail(tmpImageForThumbnail, 240, 160)) using (var thumbnail = ImageHelper.CreateThumbnail(tmpImageForThumbnail, 240, 160))
{ {
ImageOutput.SaveToStream(thumbnail, null, thumbnailStream, outputSettings); ImageIO.SaveToStream(thumbnail, null, thumbnailStream, outputSettings);
thumbnailStream.Position = 0; thumbnailStream.Position = 0;
thumbnailRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(thumbnailStream); thumbnailRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(thumbnailStream);
Log.Debug("Created RandomAccessStreamReference for the thumbnail"); Log.Debug("Created RandomAccessStreamReference for the thumbnail");
@ -172,7 +172,7 @@ namespace Greenshot.Plugin.Win10.Destinations
using (var logo = GreenshotResources.GetGreenshotIcon().ToBitmap()) using (var logo = GreenshotResources.GetGreenshotIcon().ToBitmap())
using (var logoThumbnail = ImageHelper.CreateThumbnail(logo, 30, 30)) using (var logoThumbnail = ImageHelper.CreateThumbnail(logo, 30, 30))
{ {
ImageOutput.SaveToStream(logoThumbnail, null, logoStream, outputSettings); ImageIO.SaveToStream(logoThumbnail, null, logoStream, outputSettings);
logoStream.Position = 0; logoStream.Position = 0;
logoRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(logoStream); logoRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(logoStream);
Log.Info("Created RandomAccessStreamReference for the logo"); Log.Info("Created RandomAccessStreamReference for the logo");

View file

@ -97,7 +97,7 @@ namespace Greenshot.Plugin.Win10
IEffect effect = new ResizeCanvasEffect(addedWidth, addedWidth, addedHeight, addedHeight); IEffect effect = new ResizeCanvasEffect(addedWidth, addedWidth, addedHeight, addedHeight);
outputSettings.Effects.Add(effect); outputSettings.Effects.Add(effect);
} }
ImageOutput.SaveToStream(surface, imageStream, outputSettings); ImageIO.SaveToStream(surface, imageStream, outputSettings);
imageStream.Position = 0; imageStream.Position = 0;
var randomAccessStream = imageStream.AsRandomAccessStream(); var randomAccessStream = imageStream.AsRandomAccessStream();
@ -117,7 +117,7 @@ namespace Greenshot.Plugin.Win10
OcrInformation result; OcrInformation result;
using (var imageStream = new MemoryStream()) using (var imageStream = new MemoryStream())
{ {
ImageOutput.SaveToStream(image, null, imageStream, new SurfaceOutputSettings()); ImageIO.SaveToStream(image, null, imageStream, new SurfaceOutputSettings());
imageStream.Position = 0; imageStream.Position = 0;
var randomAccessStream = imageStream.AsRandomAccessStream(); var randomAccessStream = imageStream.AsRandomAccessStream();

View file

@ -68,7 +68,7 @@ namespace Greenshot.Destinations
overwrite = true; overwrite = true;
Log.InfoFormat("Using previous filename"); Log.InfoFormat("Using previous filename");
fullPath = captureDetails.Filename; fullPath = captureDetails.Filename;
outputSettings.Format = ImageOutput.FormatForFilename(fullPath); outputSettings.Format = ImageIO.FormatForFilename(fullPath);
} }
else else
{ {
@ -87,7 +87,7 @@ namespace Greenshot.Destinations
// This is done for e.g. bugs #2974608, #2963943, #2816163, #2795317, #2789218, #3004642 // This is done for e.g. bugs #2974608, #2963943, #2816163, #2795317, #2789218, #3004642
try try
{ {
ImageOutput.Save(surface, fullPath, overwrite, outputSettings, CoreConfig.OutputFileCopyPathToClipboard); ImageIO.Save(surface, fullPath, overwrite, outputSettings, CoreConfig.OutputFileCopyPathToClipboard);
outputMade = true; outputMade = true;
} }
catch (ArgumentException ex1) catch (ArgumentException ex1)
@ -95,7 +95,7 @@ namespace Greenshot.Destinations
// Our generated filename exists, display 'save-as' // Our generated filename exists, display 'save-as'
Log.InfoFormat("Not overwriting: {0}", ex1.Message); Log.InfoFormat("Not overwriting: {0}", ex1.Message);
// when we don't allow to overwrite present a new SaveWithDialog // when we don't allow to overwrite present a new SaveWithDialog
fullPath = ImageOutput.SaveWithDialog(surface, captureDetails); fullPath = ImageIO.SaveWithDialog(surface, captureDetails);
outputMade = fullPath != null; outputMade = fullPath != null;
} }
catch (Exception ex2) catch (Exception ex2)
@ -104,7 +104,7 @@ namespace Greenshot.Destinations
// Show the problem // Show the problem
MessageBox.Show(Language.GetString(LangKey.error_save), Language.GetString(LangKey.error)); MessageBox.Show(Language.GetString(LangKey.error_save), Language.GetString(LangKey.error));
// when save failed we present a SaveWithDialog // when save failed we present a SaveWithDialog
fullPath = ImageOutput.SaveWithDialog(surface, captureDetails); fullPath = ImageIO.SaveWithDialog(surface, captureDetails);
outputMade = fullPath != null; outputMade = fullPath != null;
} }

View file

@ -57,7 +57,7 @@ namespace Greenshot.Destinations
{ {
ExportInformation exportInformation = new ExportInformation(Designation, Description); ExportInformation exportInformation = new ExportInformation(Designation, Description);
// Bug #2918756 don't overwrite path if SaveWithDialog returns null! // Bug #2918756 don't overwrite path if SaveWithDialog returns null!
var savedTo = ImageOutput.SaveWithDialog(surface, captureDetails); var savedTo = ImageIO.SaveWithDialog(surface, captureDetails);
if (savedTo != null) if (savedTo != null)
{ {
exportInformation.ExportMade = true; exportInformation.ExportMade = true;

View file

@ -1911,7 +1911,7 @@ namespace Greenshot.Forms
LOG.Error("Error closing application!", e); LOG.Error("Error closing application!", e);
} }
ImageOutput.RemoveTmpFiles(); ImageIO.RemoveTmpFiles();
// Store any open configuration changes // Store any open configuration changes
try try

View file

@ -431,12 +431,13 @@ namespace Greenshot.Helpers
if (!string.IsNullOrEmpty(filename)) if (!string.IsNullOrEmpty(filename))
{ {
// TODO: Fix that the Greenshot format needs a separate code path
try try
{ {
if (filename.ToLower().EndsWith("." + OutputFormat.greenshot)) if (filename.ToLower().EndsWith("." + OutputFormat.greenshot))
{ {
ISurface surface = new Surface(); ISurface surface = new Surface();
surface = ImageOutput.LoadGreenshotSurface(filename, surface); surface = ImageIO.LoadGreenshotSurface(filename, surface);
surface.CaptureDetails = _capture.CaptureDetails; surface.CaptureDetails = _capture.CaptureDetails;
DestinationHelper.GetDestination(EditorDestination.DESIGNATION).ExportCapture(true, surface, _capture.CaptureDetails); DestinationHelper.GetDestination(EditorDestination.DESIGNATION).ExportCapture(true, surface, _capture.CaptureDetails);
break; break;
@ -448,9 +449,10 @@ namespace Greenshot.Helpers
MessageBox.Show(Language.GetFormattedString(LangKey.error_openfile, filename)); MessageBox.Show(Language.GetFormattedString(LangKey.error_openfile, filename));
} }
// TODO: Remove Image loading for here
try try
{ {
fileImage = ImageHelper.LoadImage(filename); fileImage = ImageIO.LoadImage(filename);
} }
catch (Exception e) catch (Exception e)
{ {

View file

@ -82,7 +82,7 @@ namespace Greenshot.Helpers
/// <param name="captureDetails">ICaptureDetails</param> /// <param name="captureDetails">ICaptureDetails</param>
public static void SendImage(ISurface surface, ICaptureDetails captureDetails) public static void SendImage(ISurface surface, ICaptureDetails captureDetails)
{ {
string tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings()); string tmpFile = ImageIO.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings());
if (tmpFile == null) return; if (tmpFile == null) return;

View file

@ -187,7 +187,7 @@ namespace Greenshot.Helpers
ApplyEffects(printOutputSettings); ApplyEffects(printOutputSettings);
bool disposeImage = ImageOutput.CreateImageFromSurface(_surface, printOutputSettings, out var image); bool disposeImage = ImageIO.CreateImageFromSurface(_surface, printOutputSettings, out var image);
try try
{ {
ContentAlignment alignment = CoreConfig.OutputPrintCenter ? ContentAlignment.MiddleCenter : ContentAlignment.TopLeft; ContentAlignment alignment = CoreConfig.OutputPrintCenter ? ContentAlignment.MiddleCenter : ContentAlignment.TopLeft;