185 lines
5.3 KiB
C#
185 lines
5.3 KiB
C#
using Docnet.Core;
|
|
using Docnet.Core.Models;
|
|
|
|
Console.WriteLine("=== PDF til Billede Konverter ===");
|
|
Console.WriteLine();
|
|
|
|
Console.Write("Indtast sti til PDF-fil: ");
|
|
string? pdfPath = Console.ReadLine()?.Trim().Trim('"');
|
|
|
|
if (string.IsNullOrEmpty(pdfPath))
|
|
{
|
|
Console.WriteLine("Fejl: Ingen fil angivet.");
|
|
Console.ReadKey();
|
|
return;
|
|
}
|
|
|
|
if (!File.Exists(pdfPath))
|
|
{
|
|
Console.WriteLine($"Fejl: Filen '{pdfPath}' blev ikke fundet.");
|
|
Console.ReadKey();
|
|
return;
|
|
}
|
|
|
|
Console.Write("Indtast output-mappe (tryk Enter for samme mappe som PDF): ");
|
|
string? outputInput = Console.ReadLine()?.Trim().Trim('"');
|
|
string outputFolder = string.IsNullOrEmpty(outputInput)
|
|
? Path.GetDirectoryName(pdfPath) ?? "."
|
|
: outputInput;
|
|
|
|
Directory.CreateDirectory(outputFolder);
|
|
|
|
string fileNameWithoutExt = Path.GetFileNameWithoutExtension(pdfPath);
|
|
|
|
Console.WriteLine($"Konverterer: {pdfPath}");
|
|
|
|
using var docReader = DocLib.Instance.GetDocReader(pdfPath, new PageDimensions(1080, 1920));
|
|
|
|
int pageCount = docReader.GetPageCount();
|
|
Console.WriteLine($"Antal sider: {pageCount}");
|
|
|
|
for (int i = 0; i < pageCount; i++)
|
|
{
|
|
using var pageReader = docReader.GetPageReader(i);
|
|
|
|
int width = pageReader.GetPageWidth();
|
|
int height = pageReader.GetPageHeight();
|
|
byte[] rawBytes = pageReader.GetImage();
|
|
|
|
string outputPath = Path.Combine(outputFolder, $"{fileNameWithoutExt}_side_{i + 1}.png");
|
|
|
|
SaveAsPng(rawBytes, width, height, outputPath);
|
|
|
|
Console.WriteLine($"Gemt: {outputPath}");
|
|
}
|
|
|
|
Console.WriteLine("Konvertering fuldført!");
|
|
Console.WriteLine();
|
|
Console.WriteLine("Tryk på en tast for at afslutte...");
|
|
Console.ReadKey();
|
|
|
|
static void SaveAsPng(byte[] bgraData, int width, int height, string outputPath)
|
|
{
|
|
using var fileStream = File.Create(outputPath);
|
|
using var bw = new BinaryWriter(fileStream);
|
|
|
|
// PNG Signature
|
|
bw.Write(new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A });
|
|
|
|
// IHDR chunk
|
|
byte[] ihdrData;
|
|
using (var ms = new MemoryStream())
|
|
{
|
|
WriteBigEndianInt(ms, width);
|
|
WriteBigEndianInt(ms, height);
|
|
ms.WriteByte(8); // bit depth
|
|
ms.WriteByte(6); // color type (RGBA)
|
|
ms.WriteByte(0); // compression
|
|
ms.WriteByte(0); // filter
|
|
ms.WriteByte(0); // interlace
|
|
ihdrData = ms.ToArray();
|
|
}
|
|
WriteChunk(bw, "IHDR", ihdrData);
|
|
|
|
// Byg uncompressed data (filter-byte + RGBA pixels per scanline)
|
|
int scanlineSize = 1 + width * 4; // 1 filter byte + RGBA
|
|
byte[] uncompressed = new byte[height * scanlineSize];
|
|
|
|
for (int y = 0; y < height; y++)
|
|
{
|
|
int rowStart = y * scanlineSize;
|
|
uncompressed[rowStart] = 0; // filter type: none
|
|
|
|
for (int x = 0; x < width; x++)
|
|
{
|
|
int srcIdx = (y * width + x) * 4;
|
|
int dstIdx = rowStart + 1 + x * 4;
|
|
|
|
// BGRA til RGBA
|
|
uncompressed[dstIdx + 0] = bgraData[srcIdx + 2]; // R
|
|
uncompressed[dstIdx + 1] = bgraData[srcIdx + 1]; // G
|
|
uncompressed[dstIdx + 2] = bgraData[srcIdx + 0]; // B
|
|
uncompressed[dstIdx + 3] = bgraData[srcIdx + 3]; // A
|
|
}
|
|
}
|
|
|
|
// Komprimer med Deflate
|
|
using var compressedStream = new MemoryStream();
|
|
using (var deflate = new System.IO.Compression.DeflateStream(compressedStream, System.IO.Compression.CompressionLevel.Fastest, true))
|
|
{
|
|
deflate.Write(uncompressed);
|
|
}
|
|
byte[] compressed = compressedStream.ToArray();
|
|
|
|
// Beregn Adler-32 på uncompressed data
|
|
uint adler = Adler32(uncompressed);
|
|
|
|
// Byg zlib stream: header + compressed + adler32
|
|
using var zlibStream = new MemoryStream();
|
|
zlibStream.WriteByte(0x78); // CMF
|
|
zlibStream.WriteByte(0x01); // FLG
|
|
zlibStream.Write(compressed);
|
|
WriteBigEndianUInt(zlibStream, adler);
|
|
|
|
WriteChunk(bw, "IDAT", zlibStream.ToArray());
|
|
|
|
// IEND chunk
|
|
WriteChunk(bw, "IEND", Array.Empty<byte>());
|
|
}
|
|
|
|
static void WriteChunk(BinaryWriter bw, string type, byte[] data)
|
|
{
|
|
byte[] lengthBytes = BitConverter.GetBytes(data.Length);
|
|
if (BitConverter.IsLittleEndian) Array.Reverse(lengthBytes);
|
|
bw.Write(lengthBytes);
|
|
|
|
byte[] typeBytes = System.Text.Encoding.ASCII.GetBytes(type);
|
|
bw.Write(typeBytes);
|
|
bw.Write(data);
|
|
|
|
// CRC32
|
|
byte[] crcData = new byte[4 + data.Length];
|
|
Array.Copy(typeBytes, crcData, 4);
|
|
Array.Copy(data, 0, crcData, 4, data.Length);
|
|
uint crc = Crc32(crcData);
|
|
byte[] crcBytes = BitConverter.GetBytes(crc);
|
|
if (BitConverter.IsLittleEndian) Array.Reverse(crcBytes);
|
|
bw.Write(crcBytes);
|
|
}
|
|
|
|
static void WriteBigEndianInt(Stream stream, int value)
|
|
{
|
|
byte[] bytes = BitConverter.GetBytes(value);
|
|
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
|
|
stream.Write(bytes);
|
|
}
|
|
|
|
static void WriteBigEndianUInt(Stream stream, uint value)
|
|
{
|
|
byte[] bytes = BitConverter.GetBytes(value);
|
|
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
|
|
stream.Write(bytes);
|
|
}
|
|
|
|
static uint Crc32(byte[] data)
|
|
{
|
|
uint crc = 0xFFFFFFFF;
|
|
foreach (byte b in data)
|
|
{
|
|
crc ^= b;
|
|
for (int i = 0; i < 8; i++)
|
|
crc = (crc >> 1) ^ (0xEDB88320 & ~((crc & 1) - 1));
|
|
}
|
|
return ~crc;
|
|
}
|
|
|
|
static uint Adler32(byte[] data)
|
|
{
|
|
uint a = 1, b = 0;
|
|
foreach (byte d in data)
|
|
{
|
|
a = (a + d) % 65521;
|
|
b = (b + a) % 65521;
|
|
}
|
|
return (b << 16) | a;
|
|
}
|