Files
comic-img-compress/ImageExtractor.cs
T
marcogiuntoni 5152a87ffa V3.x
Moved functionalities to library iMGcompress-core
2025-01-23 12:53:22 +01:00

208 lines
7.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Runtime.ConstrainedExecution;
using ikvm.extensions;
using Imazen.WebP;
using iMGcompress_core;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.parser;
using SharpCompress.Archives;
using SharpCompress.Archives.Zip;
using SharpCompress.Common;
using Path = System.IO.Path;
namespace ComicCompressor
{
/// <summary>
/// Helper lass to dump all images from a PDF into separate files
/// </summary>
public class ImageExtractor : IRenderListener
{
int _currentPage = 1;
int _imageCount = 0;
readonly string _outputFilePrefix = "";
//readonly string _outputFolder;
//readonly bool _overwriteExistingFiles;
private bool Cover = true;
private Logger Logger { get; set; }
SimpleEncoder encoder = new SimpleEncoder();
public int Quality { get; set; } = 75;
private List<Stream> imageStreams = null;
public ZipArchive output = null;
public ImageExtractor(Logger logger)
{
Logger = logger;
//_outputFilePrefix = outputFilePrefix;
//_outputFolder = outputFolder;
//_overwriteExistingFiles = overwriteExistingFiles;
}
public int GetPlace(int value, int place)
{
return ((value % (place * 10)) - (value % place)) / place;
}
/// <summary>
/// Extract all images from a PDF file
/// </summary>
/// <param name="pdfPath">Full path and file name of PDF file</param>
/// <param name="outputFilePrefix">Basic name of exported files. If null then uses same name as PDF file.</param>
/// <param name="outputFolder">Where to save images. If null or empty then uses same folder as PDF file.</param>
/// <param name="overwriteExistingFiles">True to overwrite existing image files, false to skip past them</param>
/// <returns>Count of number of images extracted.</returns>
public int ExtractImagesFromFile(string pdfPath, string outputFolder, int quality = 75)
{
// Handle setting of any default values
//outputFilePrefix = outputFilePrefix ?? Path.GetFileNameWithoutExtension(pdfPath);
//outputFolder = String.IsNullOrEmpty(outputFolder) ? Path.GetDirectoryName(pdfPath) : outputFolder;
quality = quality;
Stopwatch sw = Stopwatch.StartNew();
using (var pdfReader = new PdfReader(pdfPath))
{
Logger.Log($"Processing[{pdfReader.NumberOfPages}]: " + pdfPath, LogLevel.Verbose);
if (pdfReader.IsEncrypted())
throw new ApplicationException(pdfPath + " is encrypted.");
var pdfParser = new PdfReaderContentParser(pdfReader);
output = ZipArchive.Create();
{
imageStreams = new List<Stream>();
int count = 0;
Cover = true;
while (_currentPage <= pdfReader.NumberOfPages)
{
pdfParser.ProcessContent(_currentPage, this);
_currentPage++;
count++;
if (count % 10 == 0)
{
string digit = GetPlace(count, 10).ToString();
Logger.Log(digit, LogLevel.Verbose, false);
}
}
Directory.CreateDirectory(Path.GetDirectoryName(outputFolder));
output.SaveTo(outputFolder, CompressionType.Deflate);
//output.SaveTo(outputPath, CompressionType.None);
imageStreams.ForEach(i => i.Dispose());
}
output.Dispose();
int nop = pdfReader.NumberOfPages;
if (nop == 0) nop = 1;
long elapsed = sw.ElapsedMilliseconds;
//Logger.Log($"\r\nFinished({save.ToString("0.##")}): " + pdfPath, LogLevel.Verbose);
Logger.Log($"\r\nFinished({0.ToString("0.##")} [{elapsed}][{elapsed / nop}]): " + pdfPath, LogLevel.Verbose);
}
return _imageCount;
}
private Bitmap ChangePixelFormat(Bitmap orig, System.Drawing.Imaging.PixelFormat format = System.Drawing.Imaging.PixelFormat.Format24bppRgb)
{
Bitmap clone = new Bitmap(orig.Width, orig.Height, format);
using (Graphics gr = Graphics.FromImage(clone))
{
gr.DrawImage(orig, new Rectangle(0, 0, clone.Width, clone.Height));
}
return clone;
}
#region Implementation of IRenderListener
public void BeginTextBlock()
{
}
public void EndTextBlock()
{
}
public void RenderText(TextRenderInfo renderInfo)
{
var text = renderInfo.GetText();
}
public void RenderImage(ImageRenderInfo renderInfo)
{
try
{
var type = renderInfo.GetType();
var str = renderInfo.toString();
//var ftype = renderInfo.GetFileType();
var imageObject = renderInfo.GetImage();
//var imageFileName = String.Format("{0}_{1}_{2}.{3}", _outputFilePrefix, _currentPage, _imageCount,
// imageObject.GetFileType());
var imageFileName = String.Format($"{_imageCount.ToString("0000")}");
//var imagePath = Path.Combine(_outputFolder, imageFileName);
//if (_overwriteExistingFiles || !File.Exists(imagePath))
{
var imageRawBytes = imageObject.GetImageAsBytes();
var image = imageObject.GetDrawingImage();
//File.WriteAllBytes(imagePath, imageRawBytes);
if (image.Width < 512 || image.Height < 512)
return;
if (image.PixelFormat != System.Drawing.Imaging.PixelFormat.Format24bppRgb)
{
var newBits = ChangePixelFormat((Bitmap)image);
image.Dispose();
image = newBits;
}
var ms = new MemoryStream();
encoder.Encode((Bitmap)image, ms, Quality);
if (!Cover && ms.Length < imageRawBytes.Length)
{
imageStreams.Add(ms);
output.AddEntry(imageFileName + ".webp", ms);
Logger.Log(".", LogLevel.Verbose, false);
}
else
{
var mso = new MemoryStream();
mso.Write(imageRawBytes);
output.AddEntry(imageFileName + imageObject.GetFileType(), mso);
Logger.Log(Cover ? "C" : "=", LogLevel.Verbose, false);
Cover = false;
}
}
// Subtle: Always increment even if file is not written. This ensures consistency should only some
// of a PDF file's images actually exist.
_imageCount++;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
#endregion // Implementation of IRenderListener
}
}