2021-02-25 11:14:44 +01:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Diagnostics;
|
2025-07-24 14:33:48 +02:00
|
|
|
|
using System.Diagnostics.CodeAnalysis;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
using System.Drawing;
|
|
|
|
|
|
using System.Drawing.Drawing2D;
|
|
|
|
|
|
using System.Drawing.Imaging;
|
2025-07-28 12:26:23 +02:00
|
|
|
|
using System.Globalization;
|
2025-07-28 11:25:46 +02:00
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
|
|
|
|
|
|
|
|
|
|
|
|
// Imports System.Threading
|
|
|
|
|
|
|
2025-07-28 11:25:46 +02:00
|
|
|
|
namespace MaddoShared;
|
|
|
|
|
|
|
2025-07-24 14:33:48 +02:00
|
|
|
|
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility")]
|
2025-07-28 11:25:46 +02:00
|
|
|
|
public class ImageCreatorSharp(PicSettings picSettings, ILogger<ImageCreatorSharp> logger)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2026-02-04 18:38:44 +01:00
|
|
|
|
public async Task CreateImageAsync(ImageState imgState, Image logo)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
await Task.Run(() =>
|
|
|
|
|
|
{
|
2025-07-28 11:25:46 +02:00
|
|
|
|
logger.LogInformation("File: {FileInfo} Dest: {DirectoryInfo}", imgState.WorkFile, imgState.DestDir);
|
2026-02-04 18:38:44 +01:00
|
|
|
|
PrepareVariables(imgState);
|
2025-07-28 11:25:46 +02:00
|
|
|
|
ExtractExif(imgState);
|
|
|
|
|
|
|
|
|
|
|
|
using var g = Image.FromFile(imgState.WorkFile.FullName);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
// Set extra text
|
|
|
|
|
|
SetExtraText(g, imgState);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
// Rotate image according to EXIF
|
|
|
|
|
|
ApplyRotation(g, imgState);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
// Force jpeg if option selected
|
2025-07-28 11:25:46 +02:00
|
|
|
|
var thisFormat = g.RawFormat;
|
2026-02-04 18:38:44 +01:00
|
|
|
|
if (picSettings.UsaForzaJpg)
|
2025-07-28 11:25:46 +02:00
|
|
|
|
thisFormat = ImageFormat.Jpeg;
|
|
|
|
|
|
|
|
|
|
|
|
PrepareThumbnailSize(g, imgState);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2025-07-28 11:25:46 +02:00
|
|
|
|
using var imgOutputBig = new Bitmap(g, imgState.ThumbSizeBig.Width, imgState.ThumbSizeBig.Height);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2025-07-24 14:33:48 +02:00
|
|
|
|
imgOutputBig.SetResolution(g.HorizontalResolution, g.VerticalResolution);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
// Create thumbnails
|
|
|
|
|
|
CreateThumbnails(g, imgState, imgOutputBig, thisFormat);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
AddText(g, imgState, imgOutputBig);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
AddLogo(imgOutputBig, logo);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
SavePhoto(imgOutputBig, imgState, thisFormat);
|
|
|
|
|
|
}).ConfigureAwait(false);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
var e = ex.Demystify();
|
2025-07-28 12:26:23 +02:00
|
|
|
|
logger.LogError(e, "Error in processing photo {WorkFileName}", imgState.WorkFile.Name);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 11:25:46 +02:00
|
|
|
|
private void ExtractExif(ImageState imgState)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2025-07-28 11:25:46 +02:00
|
|
|
|
using var img = SixLabors.ImageSharp.Image.Load(imgState.WorkFile.FullName);
|
|
|
|
|
|
imgState.Orientation = Orientations.TopLeft;
|
2024-10-14 19:54:29 +02:00
|
|
|
|
|
2025-07-23 15:08:25 +02:00
|
|
|
|
IExifValue<ushort> rotation = null;
|
2024-10-14 19:54:29 +02:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
var exifProfile = img.Metadata?.ExifProfile;
|
|
|
|
|
|
var found = exifProfile != null && exifProfile.TryGetValue(ExifTag.Orientation, out rotation);
|
2024-10-14 19:54:29 +02:00
|
|
|
|
|
2025-07-29 10:34:23 +02:00
|
|
|
|
if (found)
|
2025-07-23 15:08:25 +02:00
|
|
|
|
{
|
|
|
|
|
|
var intOrientation = rotation.Value.ToInt32();
|
2025-07-28 11:25:46 +02:00
|
|
|
|
imgState.Orientation = (Orientations)intOrientation;
|
2025-07-23 15:08:25 +02:00
|
|
|
|
}
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2025-07-23 15:08:25 +02:00
|
|
|
|
IExifValue<string> date = null;
|
2026-02-04 18:38:44 +01:00
|
|
|
|
var creationFound = exifProfile != null && exifProfile.TryGetValue(ExifTag.DateTimeOriginal, out date);
|
2025-07-23 15:08:25 +02:00
|
|
|
|
if (creationFound)
|
|
|
|
|
|
{
|
2025-07-29 10:34:23 +02:00
|
|
|
|
var succ = DateTime.TryParseExact(date.Value, "yyyy:MM:dd HH:mm:ss", CultureInfo.InvariantCulture,
|
|
|
|
|
|
DateTimeStyles.None, out var crDate);
|
2025-07-23 15:08:25 +02:00
|
|
|
|
if (succ)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2025-07-28 11:25:46 +02:00
|
|
|
|
imgState.CreationDate = crDate;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-07-28 11:25:46 +02:00
|
|
|
|
imgState.CreationDate = null;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
2025-07-23 15:08:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-07-28 11:25:46 +02:00
|
|
|
|
imgState.CreationDate = null;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
private void ApplyRotation(Image g, ImageState imgState)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2025-07-28 11:25:46 +02:00
|
|
|
|
imgState.FotoRuotaADestra = false;
|
|
|
|
|
|
imgState.FotoRuotaASinistra = false;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2025-07-29 10:34:23 +02:00
|
|
|
|
if (picSettings.UsaRotazioneAutomatica && g.PropertyIdList.Length > 0)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2025-07-29 10:34:23 +02:00
|
|
|
|
switch (imgState.Orientation)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2025-07-29 10:34:23 +02:00
|
|
|
|
case Orientations.BottomLeft:
|
|
|
|
|
|
case Orientations.BottomRight:
|
|
|
|
|
|
case Orientations.LeftTop:
|
|
|
|
|
|
case Orientations.LftBottom:
|
|
|
|
|
|
imgState.FotoRuotaASinistra = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case Orientations.RightBottom:
|
|
|
|
|
|
case Orientations.RightTop:
|
|
|
|
|
|
case Orientations.TopLeft:
|
|
|
|
|
|
case Orientations.TopRight:
|
|
|
|
|
|
break;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 11:25:46 +02:00
|
|
|
|
if (imgState.FotoRuotaASinistra)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
g.RotateFlip(RotateFlipType.Rotate270FlipNone);
|
2025-07-28 11:25:46 +02:00
|
|
|
|
if (imgState.FotoRuotaADestra)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
g.RotateFlip(RotateFlipType.Rotate90FlipNone);
|
|
|
|
|
|
}
|
2025-07-28 11:25:46 +02:00
|
|
|
|
|
2021-02-25 11:14:44 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// ''' Aggiunge Orario, tempo gara e altri
|
|
|
|
|
|
/// ''' </summary>
|
|
|
|
|
|
/// ''' <param name="g">Image</param>
|
2025-07-28 11:25:46 +02:00
|
|
|
|
/// <param name="imgState"></param>
|
2021-02-25 11:14:44 +01:00
|
|
|
|
/// ''' <remarks></remarks>
|
2026-02-04 18:38:44 +01:00
|
|
|
|
private void SetExtraText(Image g, ImageState imgState)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2026-02-04 18:38:44 +01:00
|
|
|
|
if (picSettings.UsaOrarioTestoApplicare || picSettings.UsaTempoGaraTestoApplicare ||
|
|
|
|
|
|
picSettings.UsaOrarioMiniatura || picSettings.TestoMin || picSettings.AggTempoGaraMin ||
|
2025-07-29 10:34:23 +02:00
|
|
|
|
picSettings.AggNumTempMin)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2025-07-28 11:25:46 +02:00
|
|
|
|
if (g.PropertyIdList.Length <= 0) return;
|
2026-02-04 18:38:44 +01:00
|
|
|
|
imgState.DataFoto = imgState.CreationDate ?? DateTime.Now;
|
2025-07-28 11:25:46 +02:00
|
|
|
|
imgState.TestoFirma = picSettings.TestoFirmaStart;
|
|
|
|
|
|
imgState.TestoFirmaV = picSettings.TestoFirmaStartV;
|
|
|
|
|
|
|
|
|
|
|
|
if (imgState.DataFoto.Year == 1) return;
|
|
|
|
|
|
imgState.TestoFirmaPiccola = imgState.DataFoto.ToShortTimeString();
|
2026-02-04 18:38:44 +01:00
|
|
|
|
if (picSettings.UsaOrarioTestoApplicare)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2025-07-28 11:25:46 +02:00
|
|
|
|
imgState.TestoFirma +=
|
|
|
|
|
|
$" {imgState.DataFoto.ToShortDateString()} {imgState.DataFoto.ToLongTimeString()}";
|
|
|
|
|
|
imgState.TestoFirmaV +=
|
|
|
|
|
|
$" {imgState.DataFoto.ToShortDateString()} {imgState.DataFoto.ToLongTimeString()}";
|
|
|
|
|
|
}
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
if (!picSettings.UsaTempoGaraTestoApplicare) return;
|
2025-07-28 12:26:23 +02:00
|
|
|
|
var diff = imgState.DataFoto - imgState.DataPartenzaI;
|
|
|
|
|
|
imgState.TestoFirma += $" {imgState.TestoOrario}{diff.Hours:00}:{diff.Minutes:00}:{diff.Seconds:00}";
|
|
|
|
|
|
imgState.TestoFirmaV += $" {imgState.TestoOrario}{diff.Hours:00}:{diff.Minutes:00}:{diff.Seconds:00}";
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-07-28 11:25:46 +02:00
|
|
|
|
imgState.TestoFirma = picSettings.TestoFirmaStart;
|
|
|
|
|
|
imgState.TestoFirmaV = picSettings.TestoFirmaStartV;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// ''' Prepara diverse variabili azzerandole, elaborandole e prendendole dalle impostazioni
|
|
|
|
|
|
/// ''' </summary>
|
|
|
|
|
|
/// ''' <remarks></remarks>
|
2026-02-04 18:38:44 +01:00
|
|
|
|
private void PrepareVariables(ImageState imgState)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2025-07-28 11:25:46 +02:00
|
|
|
|
imgState.AlphaScelta = System.Convert.ToInt32((255 * (100 - picSettings.Trasparenza) / (double)100));
|
|
|
|
|
|
imgState.TestoFirma = "";
|
|
|
|
|
|
imgState.TestoFirmaV = "";
|
|
|
|
|
|
imgState.DataPartenzaI = picSettings.DataPartenza;
|
|
|
|
|
|
imgState.TestoOrario = picSettings.TestoOrario;
|
|
|
|
|
|
if (imgState.TestoOrario.Length > 0)
|
|
|
|
|
|
imgState.TestoOrario += " ";
|
|
|
|
|
|
imgState.TestoFirmaPiccola = "";
|
|
|
|
|
|
imgState.ThumbSizeSmall = new Size();
|
|
|
|
|
|
imgState.ThumbSizeBig = new Size();
|
|
|
|
|
|
imgState.NomeFileSmall = "";
|
|
|
|
|
|
imgState.NomeFileBig2 = "";
|
|
|
|
|
|
imgState.NomeFileBig = "";
|
|
|
|
|
|
imgState.DimensioneStandard = picSettings.DimStandard;
|
|
|
|
|
|
imgState.DimensioneStandardMiniatura = picSettings.DimStandardMiniatura;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
// nomeFileSmall = Suffisso & NomeFileChild
|
|
|
|
|
|
// nomeFileBig = NomeFileChild
|
2025-07-28 11:25:46 +02:00
|
|
|
|
imgState.NomeFileSmall = picSettings.Suffisso + imgState.WorkFile.Name;
|
|
|
|
|
|
imgState.NomeFileBig = imgState.WorkFile.Name;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-29 10:34:23 +02:00
|
|
|
|
private void PrepareThumbnailSize(Image g, ImageState imgState)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
|
|
|
|
|
if (g.Width > g.Height)
|
|
|
|
|
|
{
|
2026-02-04 18:38:44 +01:00
|
|
|
|
imgState.ThumbSizeSmall = CalculateThumbnailSize(g.Width, g.Height, picSettings.LarghezzaSmall, "Larghezza");
|
2025-07-28 11:25:46 +02:00
|
|
|
|
var sizeOrig = new Size(g.Width, g.Height);
|
|
|
|
|
|
imgState.ThumbSizeBig = sizeOrig;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-02-04 18:38:44 +01:00
|
|
|
|
imgState.ThumbSizeSmall = CalculateThumbnailSize(g.Width, g.Height, picSettings.AltezzaSmall, "Altezza");
|
2025-07-28 11:25:46 +02:00
|
|
|
|
var sizeOrig = new Size(g.Width, g.Height);
|
|
|
|
|
|
imgState.ThumbSizeBig = sizeOrig;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
private void CreateThumbnails(Image sourceImage, ImageState imgState, Bitmap imgOutputBig, ImageFormat format)
|
2025-07-29 10:34:23 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (!picSettings.CreaMiniature || picSettings.AggiungiScritteMiniature)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
PrepareSignatureText(imgState);
|
|
|
|
|
|
|
|
|
|
|
|
if (IsSameDirectory(picSettings.DirectorySorgente, picSettings.DirectoryDestinazione))
|
2026-02-04 18:38:44 +01:00
|
|
|
|
UpdateFilenameWithCode(imgState);
|
2025-07-29 10:34:23 +02:00
|
|
|
|
|
|
|
|
|
|
if (ShouldRenderText())
|
2026-02-04 18:38:44 +01:00
|
|
|
|
CreateThumbnailWithText(sourceImage, imgState, imgOutputBig, format);
|
2025-07-29 10:34:23 +02:00
|
|
|
|
else
|
|
|
|
|
|
CreateSimpleThumbnail(sourceImage, imgState, format);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void PrepareSignatureText(ImageState imgState)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2025-07-28 11:25:46 +02:00
|
|
|
|
if (picSettings.TestoMin)
|
|
|
|
|
|
imgState.TestoFirmaPiccola = imgState.NomeFileBig;
|
|
|
|
|
|
else if (picSettings.AggNumTempMin)
|
|
|
|
|
|
imgState.TestoFirmaPiccola = imgState.NomeFileBig + " ";
|
2025-07-29 10:34:23 +02:00
|
|
|
|
}
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2025-07-29 10:34:23 +02:00
|
|
|
|
private bool IsSameDirectory(string dir1, string dir2) =>
|
|
|
|
|
|
string.Equals(dir1, dir2, StringComparison.OrdinalIgnoreCase);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
private void UpdateFilenameWithCode(ImageState imgState)
|
2025-07-29 10:34:23 +02:00
|
|
|
|
{
|
|
|
|
|
|
var name = imgState.NomeFileSmall;
|
|
|
|
|
|
imgState.NomeFileSmall = name[..^4] + picSettings.Codice + name[^4..];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private bool ShouldRenderText() =>
|
|
|
|
|
|
picSettings.UsaOrarioMiniatura || picSettings.TestoMin || picSettings.AggTempoGaraMin ||
|
|
|
|
|
|
picSettings.AggNumTempMin;
|
|
|
|
|
|
|
|
|
|
|
|
private void CreateSimpleThumbnail(Image image, ImageState imgState, ImageFormat format)
|
|
|
|
|
|
{
|
|
|
|
|
|
using var thumbnail = new Bitmap(image, imgState.ThumbSizeSmall.Width, imgState.ThumbSizeSmall.Height);
|
|
|
|
|
|
thumbnail.Save(Path.Combine(imgState.DestDir.FullName, imgState.NomeFileSmall), format);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
private void CreateThumbnailWithText(Image image, ImageState imgState, Bitmap sourceBitmap, ImageFormat format)
|
2025-07-29 10:34:23 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (imgState.TestoFirmaPiccola.Length == 0)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2025-07-29 10:34:23 +02:00
|
|
|
|
CreateSimpleThumbnail(image, imgState, format);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
using var imgOutputSmall = (Bitmap)sourceBitmap.Clone();
|
|
|
|
|
|
using var graphics = Graphics.FromImage(imgOutputSmall);
|
|
|
|
|
|
graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
|
|
|
|
|
|
2026-02-10 21:18:46 +01:00
|
|
|
|
// Use the user's configured font size directly
|
2025-07-29 10:34:23 +02:00
|
|
|
|
using var font1 = CreateFont(picSettings.IlFont, imgState.DimensioneStandardMiniatura, picSettings.Grassetto);
|
|
|
|
|
|
var textSize = graphics.MeasureString(imgState.TestoFirmaPiccola, font1);
|
|
|
|
|
|
|
2026-02-10 21:18:46 +01:00
|
|
|
|
// Adjust font if it's too large for the image dimensions
|
|
|
|
|
|
// Keep text height under 15% of image height to ensure proper spacing when resized
|
|
|
|
|
|
// This leaves room for margins and prevents clipping
|
|
|
|
|
|
int tempFontSize = imgState.DimensioneStandardMiniatura;
|
|
|
|
|
|
float maxTextHeight = image.Height * 0.15f;
|
|
|
|
|
|
|
|
|
|
|
|
while ((textSize.Width > image.Width * 0.95f || textSize.Height > maxTextHeight) && tempFontSize > 5)
|
|
|
|
|
|
{
|
|
|
|
|
|
tempFontSize = (tempFontSize > 20) ? tempFontSize - 5 : tempFontSize - 1;
|
|
|
|
|
|
using var tempFont = CreateFont(picSettings.IlFont, tempFontSize, picSettings.Grassetto);
|
|
|
|
|
|
textSize = graphics.MeasureString(imgState.TestoFirmaPiccola, tempFont);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Re-measure text with the final font size for accurate positioning
|
|
|
|
|
|
using var finalFont = CreateFont(picSettings.IlFont, tempFontSize, picSettings.Grassetto);
|
|
|
|
|
|
var finalTextSize = graphics.MeasureString(imgState.TestoFirmaPiccola, finalFont);
|
2025-07-29 10:34:23 +02:00
|
|
|
|
|
2026-02-10 21:18:46 +01:00
|
|
|
|
SetVerticalPosition(image.Height, finalTextSize.Height, imgState);
|
2025-07-29 10:34:23 +02:00
|
|
|
|
|
2026-02-10 21:18:46 +01:00
|
|
|
|
float xCenter = CalculateHorizontalAlignment(image.Width, finalTextSize.Width);
|
2025-07-29 10:34:23 +02:00
|
|
|
|
using var stringFormat = new StringFormat();
|
|
|
|
|
|
stringFormat.Alignment = StringAlignment.Center;
|
|
|
|
|
|
|
|
|
|
|
|
using var shadowBrush = new SolidBrush(Color.FromArgb(imgState.AlphaScelta, 0, 0, 0));
|
|
|
|
|
|
using var textBrush = new SolidBrush(Color.FromArgb(imgState.AlphaScelta, picSettings.FontColoreRGB));
|
|
|
|
|
|
|
|
|
|
|
|
DrawText(graphics, imgState, xCenter, stringFormat, shadowBrush, textBrush, finalFont);
|
|
|
|
|
|
|
|
|
|
|
|
using var finalThumb =
|
|
|
|
|
|
new Bitmap(imgOutputSmall, imgState.ThumbSizeSmall.Width, imgState.ThumbSizeSmall.Height);
|
|
|
|
|
|
finalThumb.Save(Path.Combine(imgState.DestDir.FullName, imgState.NomeFileSmall), format);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private Font CreateFont(string fontName, int size, bool bold) =>
|
|
|
|
|
|
new Font(fontName, size, bold ? FontStyle.Bold : FontStyle.Regular);
|
|
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
private int FindBestFontSize(Graphics g, string text, string fontName, int maxSize, bool bold, int maxWidth, int minSize = 5)
|
2025-07-29 10:34:23 +02:00
|
|
|
|
{
|
2026-02-04 18:38:44 +01:00
|
|
|
|
if (maxSize <= minSize) return Math.Max(minSize, maxSize);
|
2025-07-29 10:34:23 +02:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
int low = minSize;
|
|
|
|
|
|
int high = Math.Max(minSize, maxSize);
|
|
|
|
|
|
int best = minSize;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
while (low <= high)
|
2025-07-29 10:34:23 +02:00
|
|
|
|
{
|
2026-02-04 18:38:44 +01:00
|
|
|
|
int mid = (low + high) / 2;
|
|
|
|
|
|
using var testFont = CreateFont(fontName, mid, bold);
|
|
|
|
|
|
var measured = g.MeasureString(text, testFont);
|
|
|
|
|
|
if (measured.Width <= maxWidth)
|
|
|
|
|
|
{
|
|
|
|
|
|
best = mid;
|
|
|
|
|
|
low = mid + 1; // try larger
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
high = mid - 1; // too big
|
|
|
|
|
|
}
|
2025-07-29 10:34:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
return best;
|
2025-07-29 10:34:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
private void AddText(Image g, ImageState imgState, Bitmap imgOutputBig)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2025-07-24 14:33:48 +02:00
|
|
|
|
using var grPhoto = Graphics.FromImage(imgOutputBig);
|
|
|
|
|
|
grPhoto.SmoothingMode = SmoothingMode.AntiAlias;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
// Determine best base font size using a binary search (faster than decremental loop)
|
|
|
|
|
|
int availableWidth = (int)g.Width;
|
|
|
|
|
|
int targetBaseSize = imgState.DimensioneStandard > 0 ? imgState.DimensioneStandard : picSettings.DimStandard;
|
|
|
|
|
|
int bestBaseSize = FindBestFontSize(grPhoto, imgState.TestoFirma ?? string.Empty, picSettings.IlFont, targetBaseSize, picSettings.Grassetto, availableWidth);
|
|
|
|
|
|
imgState.DimensioneStandard = bestBaseSize;
|
2025-07-24 14:33:48 +02:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
// Decide final drawing size (use DimVert if rotated)
|
|
|
|
|
|
int drawSize = (imgState.FotoRuotaADestra || imgState.FotoRuotaASinistra) ? picSettings.DimVert : imgState.DimensioneStandard;
|
2025-07-29 10:34:23 +02:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
using var drawFont = CreateFont(picSettings.IlFont, drawSize, picSettings.Grassetto);
|
|
|
|
|
|
var crSize = grPhoto.MeasureString(imgState.TestoFirma ?? string.Empty, drawFont);
|
|
|
|
|
|
var larghezzaStandard = Convert.ToInt32(crSize.Width);
|
2025-07-29 10:34:23 +02:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
// Vertical positions
|
2025-07-28 11:25:46 +02:00
|
|
|
|
switch (picSettings.Posizione.ToUpper())
|
2025-07-24 14:33:48 +02:00
|
|
|
|
{
|
|
|
|
|
|
case "ALTO":
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2026-02-04 18:38:44 +01:00
|
|
|
|
imgState.YPosFromBottom = picSettings.Margine;
|
|
|
|
|
|
imgState.YPosFromBottom3 = picSettings.MargVert;
|
2025-07-24 14:33:48 +02:00
|
|
|
|
break;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-24 14:33:48 +02:00
|
|
|
|
case "BASSO":
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2025-07-29 10:34:23 +02:00
|
|
|
|
imgState.YPosFromBottom =
|
2026-02-04 18:38:44 +01:00
|
|
|
|
Convert.ToSingle((g.Height - crSize.Height - (g.Height * picSettings.Margine / 100.0)));
|
2025-07-29 10:34:23 +02:00
|
|
|
|
imgState.YPosFromBottom3 =
|
2026-02-04 18:38:44 +01:00
|
|
|
|
Convert.ToSingle((g.Height - crSize.Height - (g.Height * picSettings.MargVert / 100.0)));
|
2025-07-24 14:33:48 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2025-07-24 14:33:48 +02:00
|
|
|
|
float xCenterOfImg = 0;
|
2025-07-28 11:25:46 +02:00
|
|
|
|
using var strFormat = new StringFormat();
|
|
|
|
|
|
switch (picSettings.Allineamento.ToUpper())
|
2025-07-24 14:33:48 +02:00
|
|
|
|
{
|
|
|
|
|
|
case "SINISTRA":
|
|
|
|
|
|
{
|
2026-02-04 18:38:44 +01:00
|
|
|
|
xCenterOfImg = Convert.ToSingle((picSettings.Margine + (larghezzaStandard / (double)2)));
|
2025-07-28 11:25:46 +02:00
|
|
|
|
if ((larghezzaStandard / (double)2) > (g.Width / (double)2) - picSettings.Margine)
|
2026-02-04 18:38:44 +01:00
|
|
|
|
xCenterOfImg = Convert.ToSingle((g.Width / (double)2));
|
2025-07-24 14:33:48 +02:00
|
|
|
|
break;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-24 14:33:48 +02:00
|
|
|
|
case "CENTRO":
|
|
|
|
|
|
{
|
2026-02-04 18:38:44 +01:00
|
|
|
|
xCenterOfImg = Convert.ToSingle((g.Width / (double)2));
|
2025-07-24 14:33:48 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2025-07-24 14:33:48 +02:00
|
|
|
|
case "DESTRA":
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2025-07-29 10:34:23 +02:00
|
|
|
|
xCenterOfImg =
|
2026-02-04 18:38:44 +01:00
|
|
|
|
Convert.ToSingle((g.Width - picSettings.Margine - (larghezzaStandard / (double)2)));
|
2025-07-28 11:25:46 +02:00
|
|
|
|
if ((larghezzaStandard / (double)2) > (g.Width / (double)2) - picSettings.Margine)
|
2026-02-04 18:38:44 +01:00
|
|
|
|
xCenterOfImg = Convert.ToSingle((g.Width / (double)2));
|
2025-07-24 14:33:48 +02:00
|
|
|
|
break;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
2025-07-24 14:33:48 +02:00
|
|
|
|
}
|
2025-07-29 10:34:23 +02:00
|
|
|
|
|
2025-07-28 11:25:46 +02:00
|
|
|
|
strFormat.Alignment = StringAlignment.Center;
|
2025-07-24 14:33:48 +02:00
|
|
|
|
|
2025-07-28 11:25:46 +02:00
|
|
|
|
using var semiTransBrush2 = new SolidBrush(Color.FromArgb(imgState.AlphaScelta, 0, 0, 0));
|
|
|
|
|
|
using var semiTransBrush = new SolidBrush(Color.FromArgb(imgState.AlphaScelta, picSettings.FontColoreRGB));
|
2025-07-24 14:33:48 +02:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
// write text (NomeFileBig)
|
2025-07-28 11:25:46 +02:00
|
|
|
|
if (picSettings.TestoNome)
|
2025-07-24 14:33:48 +02:00
|
|
|
|
{
|
2026-02-04 18:38:44 +01:00
|
|
|
|
if (picSettings.NomeData && g.PropertyIdList.Length > 0)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2026-02-04 18:38:44 +01:00
|
|
|
|
imgState.DataFoto = imgState.CreationDate ?? DateTime.Now;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
grPhoto.DrawString((imgState.NomeFileBig + " " + imgState.DataFoto.ToShortDateString()), drawFont,
|
2025-07-29 10:34:23 +02:00
|
|
|
|
semiTransBrush2, new PointF(xCenterOfImg + 1, imgState.YPosFromBottom + 1), strFormat);
|
2026-02-04 18:38:44 +01:00
|
|
|
|
grPhoto.DrawString((imgState.NomeFileBig + " " + imgState.DataFoto.ToShortDateString()), drawFont,
|
2025-07-29 10:34:23 +02:00
|
|
|
|
semiTransBrush, new PointF(xCenterOfImg, imgState.YPosFromBottom), strFormat);
|
2025-07-24 14:33:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-02-04 18:38:44 +01:00
|
|
|
|
grPhoto.DrawString(imgState.NomeFileBig, drawFont, semiTransBrush2,
|
2025-07-29 10:34:23 +02:00
|
|
|
|
new PointF(xCenterOfImg + 1, imgState.YPosFromBottom + 1), strFormat);
|
2026-02-04 18:38:44 +01:00
|
|
|
|
grPhoto.DrawString(imgState.NomeFileBig, drawFont, semiTransBrush,
|
2025-07-29 10:34:23 +02:00
|
|
|
|
new PointF(xCenterOfImg, imgState.YPosFromBottom), strFormat);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
2025-07-24 14:33:48 +02:00
|
|
|
|
}
|
2026-02-04 18:38:44 +01:00
|
|
|
|
else
|
2025-07-24 14:33:48 +02:00
|
|
|
|
{
|
2026-02-04 18:38:44 +01:00
|
|
|
|
if (imgState.FotoRuotaADestra || imgState.FotoRuotaASinistra)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2026-02-04 18:38:44 +01:00
|
|
|
|
if (!picSettings.TestoMin)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2026-02-04 18:38:44 +01:00
|
|
|
|
grPhoto.DrawString(imgState.TestoFirmaV, drawFont, semiTransBrush2,
|
2025-07-29 10:34:23 +02:00
|
|
|
|
new PointF(xCenterOfImg + 1, imgState.YPosFromBottom3 + 1), strFormat);
|
2026-02-04 18:38:44 +01:00
|
|
|
|
grPhoto.DrawString(imgState.TestoFirmaV, drawFont, semiTransBrush,
|
2025-07-29 10:34:23 +02:00
|
|
|
|
new PointF(xCenterOfImg, imgState.YPosFromBottom3), strFormat);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
2025-07-29 10:34:23 +02:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
if (picSettings.TestoMin)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2026-02-04 18:38:44 +01:00
|
|
|
|
grPhoto.DrawString(imgState.TestoFirmaV, drawFont, semiTransBrush2,
|
2025-07-29 10:34:23 +02:00
|
|
|
|
new PointF(xCenterOfImg + 1, imgState.YPosFromBottom4 + 1), strFormat);
|
2026-02-04 18:38:44 +01:00
|
|
|
|
grPhoto.DrawString(imgState.TestoFirmaV, drawFont, semiTransBrush,
|
2025-07-29 10:34:23 +02:00
|
|
|
|
new PointF(xCenterOfImg, imgState.YPosFromBottom4), strFormat);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-07-24 14:33:48 +02:00
|
|
|
|
else
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2026-02-04 18:38:44 +01:00
|
|
|
|
grPhoto.DrawString(imgState.TestoFirma, drawFont, semiTransBrush2,
|
2025-07-29 10:34:23 +02:00
|
|
|
|
new PointF(xCenterOfImg + 1, imgState.YPosFromBottom + 1), strFormat);
|
2026-02-04 18:38:44 +01:00
|
|
|
|
grPhoto.DrawString(imgState.TestoFirma, drawFont, semiTransBrush,
|
2025-07-29 10:34:23 +02:00
|
|
|
|
new PointF(xCenterOfImg, imgState.YPosFromBottom), strFormat);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-29 10:34:23 +02:00
|
|
|
|
if (string.Equals(picSettings.DirectorySorgente, picSettings.DirectoryDestinazione,
|
|
|
|
|
|
StringComparison.OrdinalIgnoreCase))
|
2025-07-24 14:33:48 +02:00
|
|
|
|
{
|
2025-07-28 11:25:46 +02:00
|
|
|
|
imgState.NomeFileBig2 = imgState.NomeFileBig;
|
|
|
|
|
|
imgState.NomeFileBig = $"{imgState.NomeFileBig[..^4]}{picSettings.Codice}{imgState.NomeFileBig[^4..]}";
|
2025-07-24 14:33:48 +02:00
|
|
|
|
}
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
private void AddLogo(Bitmap imgOutputBig, Image logo)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2026-02-04 18:38:44 +01:00
|
|
|
|
// Skip if no logo provided
|
|
|
|
|
|
if (logo is null) return;
|
|
|
|
|
|
|
|
|
|
|
|
// Load check (use short-circuit &&)
|
|
|
|
|
|
if (!(picSettings.LogoAggiungi && File.Exists(picSettings.LogoNomeFile))) return;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2025-07-28 11:25:46 +02:00
|
|
|
|
var logoColoreTrasparente = Color.White;
|
2025-07-24 14:33:48 +02:00
|
|
|
|
|
2025-07-28 11:25:46 +02:00
|
|
|
|
// * Load this Bitmap into a new Graphic Object
|
|
|
|
|
|
using var grWatermark = Graphics.FromImage(imgOutputBig);
|
|
|
|
|
|
using ImageAttributes imageAttributes = new ImageAttributes();
|
2025-07-24 14:33:48 +02:00
|
|
|
|
|
2025-07-28 11:25:46 +02:00
|
|
|
|
// * The first step replace the background color with one that is transparent (Alpha=0, R=0, G=0, B=0)
|
|
|
|
|
|
var colorMap = new ColorMap
|
|
|
|
|
|
{
|
2025-07-24 14:33:48 +02:00
|
|
|
|
// * background this will be the color we search for and replace with transparency
|
2025-07-28 11:25:46 +02:00
|
|
|
|
OldColor = logoColoreTrasparente,
|
|
|
|
|
|
NewColor = Color.FromArgb(0, 0, 0, 0)
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
var remapTable = new[] { colorMap };
|
|
|
|
|
|
imageAttributes.SetRemapTable(remapTable, ColorAdjustType.Bitmap);
|
|
|
|
|
|
|
|
|
|
|
|
// * The second color manipulation is used to change the opacity by setting the 3rd row and 3rd column to 0.3f
|
2026-02-10 21:18:46 +01:00
|
|
|
|
// Parse transparency safely (default to 100 if parsing fails)
|
|
|
|
|
|
if (!int.TryParse(picSettings.LogoTrasparenza, out var logoTransparencyValue))
|
|
|
|
|
|
{
|
|
|
|
|
|
logoTransparencyValue = 100;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-29 10:34:23 +02:00
|
|
|
|
var colorMatrixElements = new[]
|
|
|
|
|
|
{
|
2026-02-04 18:38:44 +01:00
|
|
|
|
new[] { 1.0F, 0.0F, 0.0F, 0.0F, 0.0F }, new[] { 0.0F, 1.0F, 0.0F, 0.0F, 0.0F },
|
|
|
|
|
|
new[] { 0.0F, 0.0F, 1.0F, 0.0F, 0.0F },
|
2026-02-10 21:18:46 +01:00
|
|
|
|
new[] { 0.0F, 0.0F, 0.0F, System.Convert.ToSingle(logoTransparencyValue) / 100F, 0.0F },
|
2026-02-04 18:38:44 +01:00
|
|
|
|
new[] { 0.0F, 0.0F, 0.0F, 0.0F, 1.0F }
|
2025-07-29 10:34:23 +02:00
|
|
|
|
};
|
2025-07-28 11:25:46 +02:00
|
|
|
|
var wmColorMatrix = new ColorMatrix(colorMatrixElements);
|
|
|
|
|
|
imageAttributes.SetColorMatrix(wmColorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
|
|
|
|
|
|
|
|
|
|
|
|
var fotoLogoH = picSettings.LogoAltezza;
|
|
|
|
|
|
var fotoLogoW = picSettings.LogoLarghezza;
|
|
|
|
|
|
var fattoreAlt = logo.Height / (double)fotoLogoH;
|
|
|
|
|
|
var fattoreLarg = logo.Width / (double)fotoLogoW;
|
2025-07-29 10:34:23 +02:00
|
|
|
|
var nuovaSize = fattoreLarg > fattoreAlt
|
2026-02-04 18:38:44 +01:00
|
|
|
|
? CalculateThumbnailSize(logo.Width, logo.Height, fotoLogoW, "Larghezza")
|
|
|
|
|
|
: CalculateThumbnailSize(logo.Width, logo.Height, fotoLogoH, "Altezza");
|
2025-07-28 11:25:46 +02:00
|
|
|
|
|
2026-02-10 21:18:46 +01:00
|
|
|
|
// Guard against null/empty LogoMargine and percentage parsing
|
|
|
|
|
|
var logoMargineStr = picSettings.LogoMargine ?? string.Empty;
|
|
|
|
|
|
var inPercentualeL = logoMargineStr.EndsWith('%');
|
|
|
|
|
|
var margineL = 0;
|
|
|
|
|
|
if (inPercentualeL)
|
|
|
|
|
|
{
|
|
|
|
|
|
var trimmed = logoMargineStr.TrimEnd('%');
|
|
|
|
|
|
if (!int.TryParse(trimmed, out margineL)) margineL = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!int.TryParse(logoMargineStr, out margineL)) margineL = 0;
|
|
|
|
|
|
}
|
2025-07-29 10:34:23 +02:00
|
|
|
|
var margineUsato =
|
|
|
|
|
|
inPercentualeL ? System.Convert.ToInt32(imgOutputBig.Height * margineL / (double)100) : margineL;
|
2025-07-28 11:25:46 +02:00
|
|
|
|
|
|
|
|
|
|
int xPosOfWm = 0;
|
|
|
|
|
|
int yPosOfWm = 0;
|
2026-02-10 21:18:46 +01:00
|
|
|
|
var logoH = (picSettings.LogoPosizioneH ?? "NESSUNA").ToUpperInvariant();
|
|
|
|
|
|
var logoV = (picSettings.LogoPosizioneV ?? "NESSUNA").ToUpperInvariant();
|
|
|
|
|
|
switch (logoH)
|
2025-07-28 11:25:46 +02:00
|
|
|
|
{
|
|
|
|
|
|
case "SINISTRA":
|
|
|
|
|
|
case "NESSUNA":
|
2025-07-24 14:33:48 +02:00
|
|
|
|
{
|
2025-07-28 11:25:46 +02:00
|
|
|
|
xPosOfWm = margineUsato;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2025-07-28 11:25:46 +02:00
|
|
|
|
case "CENTRO":
|
|
|
|
|
|
{
|
|
|
|
|
|
xPosOfWm = System.Convert.ToInt32((imgOutputBig.Width - nuovaSize.Width) / (double)2);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2025-07-28 11:25:46 +02:00
|
|
|
|
case "DESTRA":
|
|
|
|
|
|
{
|
|
|
|
|
|
xPosOfWm = ((imgOutputBig.Width - nuovaSize.Width) - margineUsato);
|
|
|
|
|
|
break;
|
2025-07-24 14:33:48 +02:00
|
|
|
|
}
|
2025-07-28 11:25:46 +02:00
|
|
|
|
}
|
2025-07-29 10:34:23 +02:00
|
|
|
|
|
2026-02-10 21:18:46 +01:00
|
|
|
|
switch (logoV)
|
2025-07-28 11:25:46 +02:00
|
|
|
|
{
|
|
|
|
|
|
case "ALTO":
|
|
|
|
|
|
case "NESSUNA":
|
2025-07-24 14:33:48 +02:00
|
|
|
|
{
|
2025-07-28 11:25:46 +02:00
|
|
|
|
yPosOfWm = margineUsato;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2025-07-28 11:25:46 +02:00
|
|
|
|
case "CENTRO":
|
|
|
|
|
|
{
|
|
|
|
|
|
yPosOfWm = System.Convert.ToInt32((imgOutputBig.Height - nuovaSize.Height) / (double)2);
|
|
|
|
|
|
break;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
2025-07-24 14:33:48 +02:00
|
|
|
|
|
2025-07-28 11:25:46 +02:00
|
|
|
|
case "BASSO":
|
|
|
|
|
|
{
|
|
|
|
|
|
yPosOfWm = ((imgOutputBig.Height - nuovaSize.Height) - margineUsato);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
2025-07-28 11:25:46 +02:00
|
|
|
|
|
2025-07-29 10:34:23 +02:00
|
|
|
|
grWatermark.DrawImage(logo, new Rectangle(xPosOfWm, yPosOfWm, nuovaSize.Width, nuovaSize.Height), 0, 0,
|
|
|
|
|
|
logo.Width, logo.Height, GraphicsUnit.Pixel, imageAttributes);
|
2025-07-28 11:25:46 +02:00
|
|
|
|
//grWatermark.Dispose();
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
private void SavePhoto(Bitmap imgOutputBig, ImageState imgState, ImageFormat thisFormat)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2025-07-28 14:45:03 +02:00
|
|
|
|
var fileName = Path.Combine(imgState.DestDir.FullName, imgState.NomeFileBig);
|
2025-07-29 10:34:23 +02:00
|
|
|
|
|
2025-07-24 14:33:48 +02:00
|
|
|
|
using var image1Stream = new MemoryStream();
|
2025-07-28 11:25:46 +02:00
|
|
|
|
if (picSettings.FotoGrandeDimOrigina == false)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2025-07-24 14:33:48 +02:00
|
|
|
|
// attenzione non controlla se è png
|
|
|
|
|
|
// imgOutputBig.Save(Path.Combine(_DestDir.FullName, "Temp_" & NomeFileBig), thisFormat)
|
|
|
|
|
|
if (thisFormat.Equals(ImageFormat.Jpeg))
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2026-02-04 18:38:44 +01:00
|
|
|
|
MakeImageCustomQuality(imgOutputBig, image1Stream, picSettings.JpegQuality);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
2025-07-28 10:22:08 +02:00
|
|
|
|
//SalvaImmagineCustomQuality(imgOutputBig, Path.Combine(DestDir.FullName, "Temp_" + NomeFileBig), _picSettings.jpegQuality);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-07-24 14:33:48 +02:00
|
|
|
|
imgOutputBig.Save(image1Stream, thisFormat);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
2025-07-29 10:34:23 +02:00
|
|
|
|
|
2025-07-24 14:33:48 +02:00
|
|
|
|
//imgOutputBig.Save(Path.Combine(DestDir.FullName, "Temp_" + NomeFileBig), thisFormat);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
image1Stream.Seek(0, SeekOrigin.Begin);
|
2025-07-24 14:33:48 +02:00
|
|
|
|
using var g2 = Image.FromStream(image1Stream);
|
2025-07-29 10:34:23 +02:00
|
|
|
|
imgState.ThumbSizeBig = g2.Width > g2.Height
|
2026-02-04 18:38:44 +01:00
|
|
|
|
? CalculateThumbnailSize(g2.Width, g2.Height, picSettings.LarghezzaBig, "Larghezza")
|
|
|
|
|
|
: CalculateThumbnailSize(g2.Width, g2.Height, picSettings.AltezzaBig, "Altezza");
|
2025-07-28 11:25:46 +02:00
|
|
|
|
using var imgOutputBig2 = new Bitmap(g2, imgState.ThumbSizeBig.Width, imgState.ThumbSizeBig.Height);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2025-07-28 14:45:03 +02:00
|
|
|
|
if (!picSettings.OverwriteFiles && File.Exists(fileName))
|
|
|
|
|
|
{
|
|
|
|
|
|
logger.LogInformation("Saltata foto {FileName}, esiste", fileName);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (thisFormat.Equals(ImageFormat.Jpeg))
|
2026-02-04 18:38:44 +01:00
|
|
|
|
SaveImageCustomQuality(imgOutputBig2, fileName, picSettings.JpegQuality);
|
2025-07-28 14:45:03 +02:00
|
|
|
|
else
|
|
|
|
|
|
imgOutputBig2.Save(fileName, thisFormat);
|
|
|
|
|
|
}
|
2025-07-24 14:33:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-07-28 14:45:03 +02:00
|
|
|
|
if (!picSettings.OverwriteFiles && File.Exists(fileName))
|
|
|
|
|
|
{
|
|
|
|
|
|
logger.LogInformation("Saltata foto {FileName}, esiste", fileName);
|
|
|
|
|
|
}
|
2025-07-24 14:33:48 +02:00
|
|
|
|
else
|
2025-07-28 14:45:03 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (thisFormat.Equals(ImageFormat.Jpeg))
|
2026-02-04 18:38:44 +01:00
|
|
|
|
SaveImageCustomQuality(imgOutputBig, fileName, picSettings.JpegQuality);
|
2025-07-28 14:45:03 +02:00
|
|
|
|
else
|
|
|
|
|
|
imgOutputBig.Save(fileName, thisFormat);
|
|
|
|
|
|
}
|
2025-07-24 14:33:48 +02:00
|
|
|
|
}
|
2025-07-29 10:34:23 +02:00
|
|
|
|
|
2025-07-24 14:33:48 +02:00
|
|
|
|
image1Stream.Seek(0, SeekOrigin.Begin);
|
|
|
|
|
|
|
2025-07-28 11:25:46 +02:00
|
|
|
|
if (!picSettings.CreaMiniature) return;
|
|
|
|
|
|
if (!picSettings.AggiungiScritteMiniature) return;
|
2025-07-29 10:34:23 +02:00
|
|
|
|
|
2025-07-28 11:25:46 +02:00
|
|
|
|
using var g1 = picSettings.FotoGrandeDimOrigina ? (Image)imgOutputBig.Clone() : Image.FromStream(image1Stream);
|
2025-07-29 10:34:23 +02:00
|
|
|
|
|
2025-07-28 14:45:03 +02:00
|
|
|
|
using var imgOutputSmall = new Bitmap(g1, imgState.ThumbSizeSmall.Width, imgState.ThumbSizeSmall.Height);
|
2025-07-29 10:34:23 +02:00
|
|
|
|
|
|
|
|
|
|
if (string.Equals(picSettings.DirectorySorgente, picSettings.DirectoryDestinazione,
|
|
|
|
|
|
StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
|
imgState.NomeFileSmall = imgState.NomeFileSmall.Substring(0, imgState.NomeFileSmall.Length - 4) +
|
|
|
|
|
|
picSettings.Codice +
|
|
|
|
|
|
imgState.NomeFileSmall.Substring(imgState.NomeFileSmall.Length - 4);
|
|
|
|
|
|
|
2025-07-28 14:45:03 +02:00
|
|
|
|
var tnFileName = Path.Combine(imgState.DestDir.FullName, imgState.NomeFileSmall);
|
2025-07-24 14:33:48 +02:00
|
|
|
|
|
2025-07-28 14:45:03 +02:00
|
|
|
|
if (!picSettings.OverwriteFiles && File.Exists(tnFileName))
|
|
|
|
|
|
{
|
|
|
|
|
|
logger.LogInformation("Saltata miniatura foto {TnFileName}, esiste", tnFileName);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (thisFormat.Equals(ImageFormat.Jpeg))
|
2026-02-04 18:38:44 +01:00
|
|
|
|
SaveImageCustomQuality(imgOutputSmall, tnFileName, picSettings.JpegQualityMin);
|
2025-07-28 14:45:03 +02:00
|
|
|
|
else
|
|
|
|
|
|
imgOutputSmall.Save(tnFileName, thisFormat);
|
|
|
|
|
|
}
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
private void SaveImageCustomQuality(Bitmap imageToSave, string nomeFileFinale, long quality)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2025-07-28 11:25:46 +02:00
|
|
|
|
var jgpEncoder = GetEncoder(ImageFormat.Jpeg);
|
|
|
|
|
|
var myEncoder = System.Drawing.Imaging.Encoder.Quality;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2025-07-28 11:25:46 +02:00
|
|
|
|
using var myEncoderParameters = new EncoderParameters(1);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
var myEncoderParameter = new EncoderParameter(myEncoder, quality);
|
2025-07-28 11:25:46 +02:00
|
|
|
|
myEncoderParameters.Param[0] = myEncoderParameter;
|
|
|
|
|
|
imageToSave.Save(nomeFileFinale, jgpEncoder, myEncoderParameters);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
//imageToSave.Dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
private void MakeImageCustomQuality(Bitmap imageToSave, Stream destinationStream, long quality)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
2025-07-28 11:25:46 +02:00
|
|
|
|
var jgpEncoder = GetEncoder(ImageFormat.Jpeg);
|
|
|
|
|
|
var myEncoder = System.Drawing.Imaging.Encoder.Quality;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2025-07-28 11:25:46 +02:00
|
|
|
|
using var myEncoderParameters = new EncoderParameters(1);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
var myEncoderParameter = new EncoderParameter(myEncoder, quality);
|
2025-07-28 11:25:46 +02:00
|
|
|
|
myEncoderParameters.Param[0] = myEncoderParameter;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
destinationStream.Seek(0, SeekOrigin.Begin);
|
2025-07-28 11:25:46 +02:00
|
|
|
|
imageToSave.Save(destinationStream, jgpEncoder, myEncoderParameters);
|
2021-02-25 11:14:44 +01:00
|
|
|
|
//imageToSave.Dispose();
|
|
|
|
|
|
}
|
2025-07-29 10:34:23 +02:00
|
|
|
|
|
2021-02-25 11:14:44 +01:00
|
|
|
|
private ImageCodecInfo GetEncoder(ImageFormat format)
|
|
|
|
|
|
{
|
2025-07-28 11:25:46 +02:00
|
|
|
|
var codecs = ImageCodecInfo.GetImageDecoders();
|
2025-07-29 10:34:23 +02:00
|
|
|
|
|
2021-02-25 11:14:44 +01:00
|
|
|
|
foreach (var codec in codecs)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (codec.FormatID == format.Guid)
|
|
|
|
|
|
return codec;
|
|
|
|
|
|
}
|
2025-07-29 10:34:23 +02:00
|
|
|
|
|
|
|
|
|
|
return null /* TODO Change to default(_) if this is not a reference type */;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
2025-07-29 10:34:23 +02:00
|
|
|
|
|
2021-02-25 11:14:44 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// ''' Calculate the Size of the New image
|
|
|
|
|
|
/// ''' </summary>
|
|
|
|
|
|
/// ''' <param name="currentwidth">Larghezza</param>
|
|
|
|
|
|
/// ''' <param name="currentheight">Altezza</param>
|
2025-07-28 11:25:46 +02:00
|
|
|
|
/// ''' <param name="maxPixel"></param>
|
|
|
|
|
|
/// ''' <param name="tipoSize"></param>
|
2021-02-25 11:14:44 +01:00
|
|
|
|
/// ''' <returns></returns>
|
|
|
|
|
|
/// ''' <remarks></remarks>
|
2026-02-04 18:38:44 +01:00
|
|
|
|
private Size CalculateThumbnailSize(int currentwidth, int currentheight, int maxPixel, string tipoSize)
|
2021-02-25 11:14:44 +01:00
|
|
|
|
{
|
|
|
|
|
|
// e
|
|
|
|
|
|
// *** Larghezza, Altezza, Auto
|
|
|
|
|
|
|
|
|
|
|
|
double tempMultiplier;
|
|
|
|
|
|
|
2025-07-28 11:25:46 +02:00
|
|
|
|
if (tipoSize.ToUpper() == "Larghezza".ToUpper())
|
|
|
|
|
|
tempMultiplier = maxPixel / (double)currentwidth;
|
|
|
|
|
|
else if (tipoSize.ToUpper() == "Altezza".ToUpper())
|
|
|
|
|
|
tempMultiplier = maxPixel / (double)currentheight;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
else if (currentheight > currentwidth)
|
2025-07-28 11:25:46 +02:00
|
|
|
|
tempMultiplier = maxPixel / (double)currentheight;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
else
|
2025-07-28 11:25:46 +02:00
|
|
|
|
tempMultiplier = maxPixel / (double)currentwidth;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2025-07-29 10:34:23 +02:00
|
|
|
|
var newSize = new Size(System.Convert.ToInt32(currentwidth * tempMultiplier),
|
|
|
|
|
|
System.Convert.ToInt32(currentheight * tempMultiplier));
|
2021-02-25 11:14:44 +01:00
|
|
|
|
|
2025-07-28 11:25:46 +02:00
|
|
|
|
return newSize;
|
2021-02-25 11:14:44 +01:00
|
|
|
|
}
|
2026-02-04 18:38:44 +01:00
|
|
|
|
|
|
|
|
|
|
private void SetVerticalPosition(int imgHeight, float textHeight, ImageState imgState)
|
|
|
|
|
|
{
|
2026-02-10 21:18:46 +01:00
|
|
|
|
// Use 1% of image height as minimum margin, or 10px, whichever is larger
|
|
|
|
|
|
float minMargin = Math.Max(10f, imgHeight * 0.01f);
|
|
|
|
|
|
|
2026-02-04 18:38:44 +01:00
|
|
|
|
switch (picSettings.Posizione.ToUpper())
|
|
|
|
|
|
{
|
|
|
|
|
|
case "ALTO":
|
2026-02-10 21:18:46 +01:00
|
|
|
|
imgState.YPosFromBottom1 = Math.Max(minMargin, picSettings.Margine);
|
|
|
|
|
|
imgState.YPosFromBottom4 = Math.Max(minMargin, picSettings.MargVert);
|
2026-02-04 18:38:44 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case "BASSO":
|
2026-02-10 21:18:46 +01:00
|
|
|
|
var bottomMargin1 = (float)(imgHeight * picSettings.Margine / 100.0);
|
|
|
|
|
|
var bottomMargin4 = (float)(imgHeight * picSettings.MargVert / 100.0);
|
|
|
|
|
|
|
|
|
|
|
|
// Position from bottom: bottom edge of text at desired margin from bottom
|
|
|
|
|
|
// Y = imageHeight - textHeight - bottomMargin
|
|
|
|
|
|
var desiredY1 = imgHeight - textHeight - bottomMargin1;
|
|
|
|
|
|
var desiredY4 = imgHeight - textHeight - bottomMargin4;
|
|
|
|
|
|
|
|
|
|
|
|
// Ensure text stays completely within bounds:
|
|
|
|
|
|
// - Top edge must be >= minMargin (not clipped at top)
|
|
|
|
|
|
// - Bottom edge must be <= imgHeight - minMargin (not clipped at bottom)
|
|
|
|
|
|
var maxAllowedY1 = imgHeight - textHeight - minMargin; // Maximum Y to keep bottom margin
|
|
|
|
|
|
var maxAllowedY4 = imgHeight - textHeight - minMargin;
|
|
|
|
|
|
|
|
|
|
|
|
imgState.YPosFromBottom1 = Math.Max(minMargin, Math.Min(desiredY1, maxAllowedY1));
|
|
|
|
|
|
imgState.YPosFromBottom4 = Math.Max(minMargin, Math.Min(desiredY4, maxAllowedY4));
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case "CENTRO":
|
|
|
|
|
|
default:
|
|
|
|
|
|
// Center the text vertically
|
|
|
|
|
|
var centeredY = (imgHeight - textHeight) / 2f;
|
|
|
|
|
|
// Clamp to ensure margins are respected
|
|
|
|
|
|
imgState.YPosFromBottom1 = Math.Max(minMargin, Math.Min(centeredY, imgHeight - textHeight - minMargin));
|
|
|
|
|
|
imgState.YPosFromBottom4 = imgState.YPosFromBottom1;
|
2026-02-04 18:38:44 +01:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private float CalculateHorizontalAlignment(int imgWidth, float textWidth)
|
|
|
|
|
|
{
|
|
|
|
|
|
double halfWidth = textWidth / 2.0;
|
|
|
|
|
|
|
|
|
|
|
|
return picSettings.Allineamento.ToUpper() switch
|
|
|
|
|
|
{
|
|
|
|
|
|
"SINISTRA" => (float)Math.Min(picSettings.Margine + halfWidth, imgWidth / 2.0),
|
|
|
|
|
|
"DESTRA" => (float)Math.Max(imgWidth - picSettings.Margine - halfWidth, imgWidth / 2.0),
|
|
|
|
|
|
_ => imgWidth / 2.0f, // CENTRO or default
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void DrawText(Graphics g, ImageState imgState, float x, StringFormat format,
|
|
|
|
|
|
Brush shadowBrush, Brush textBrush, Font font)
|
|
|
|
|
|
{
|
|
|
|
|
|
string content = imgState.TestoFirmaPiccola;
|
|
|
|
|
|
|
|
|
|
|
|
if (picSettings.TestoMin)
|
|
|
|
|
|
{
|
|
|
|
|
|
content = imgState.NomeFileBig;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (picSettings.AggTempoGaraMin && picSettings.UsaTempoGaraTestoApplicare)
|
|
|
|
|
|
{
|
|
|
|
|
|
content = FormatTimeText(imgState, includeFileName: false);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (picSettings.AggNumTempMin)
|
|
|
|
|
|
{
|
|
|
|
|
|
content = FormatTimeText(imgState, includeFileName: true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var offset = new PointF(x + 1, imgState.YPosFromBottom1 + 1);
|
|
|
|
|
|
var actual = new PointF(x, imgState.YPosFromBottom1);
|
|
|
|
|
|
|
|
|
|
|
|
g.DrawString(content, font, shadowBrush, offset, format);
|
|
|
|
|
|
g.DrawString(content, font, textBrush, actual, format);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private string FormatTimeText(ImageState imgState, bool includeFileName)
|
|
|
|
|
|
{
|
|
|
|
|
|
var diff = imgState.DataPartenzaI - imgState.DataFoto;
|
|
|
|
|
|
var ticks = (long)(diff.TotalSeconds * 10000000);
|
|
|
|
|
|
var time = new TimeSpan(ticks);
|
|
|
|
|
|
|
|
|
|
|
|
var formatted = $"{imgState.TestoOrario}{time:hh\\:mm\\:ss}";
|
|
|
|
|
|
return includeFileName
|
|
|
|
|
|
? $"{imgState.NomeFileBig}{Environment.NewLine}{formatted}"
|
|
|
|
|
|
: Environment.NewLine + formatted;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|