Adds basic POC for PDFToImage
This commit is contained in:
parent
3aeae6315b
commit
4f6337de6b
3 changed files with 223 additions and 0 deletions
24
POC/PdfToImage.sln
Normal file
24
POC/PdfToImage.sln
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.5.2.0
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfToImage", "PdfToImage\PdfToImage.csproj", "{515835BA-A8E0-C3F6-57E3-24410428E129}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{515835BA-A8E0-C3F6-57E3-24410428E129}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{515835BA-A8E0-C3F6-57E3-24410428E129}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{515835BA-A8E0-C3F6-57E3-24410428E129}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{515835BA-A8E0-C3F6-57E3-24410428E129}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {2F6D3A0F-356F-483E-863D-C672AEE8323E}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
14
POC/PdfToImage/PdfToImage.csproj
Normal file
14
POC/PdfToImage/PdfToImage.csproj
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Docnet.Core" Version="2.6.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
185
POC/PdfToImage/Program.cs
Normal file
185
POC/PdfToImage/Program.cs
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue