itext pdfimage library
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
using iText.Kernel.Pdf;
|
||||
using iText.Kernel.Pdf.Canvas.Parser;
|
||||
using iText.Kernel.Pdf.Canvas.Parser.Listener;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using iText.IO.Font;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Drawing.Text;
|
||||
|
||||
namespace itext.pdfimage.Extensions
|
||||
{
|
||||
public static class ConvertExtensions
|
||||
{
|
||||
public static IEnumerable<Bitmap> ConvertToBitmaps(this PdfDocument pdfDocument)
|
||||
{
|
||||
var converter = new PdfToImageConverter();
|
||||
return converter.ConvertToBitmaps(pdfDocument);
|
||||
}
|
||||
|
||||
public static Bitmap ConvertPageToBitmap(this PdfPage pdfPage)
|
||||
{
|
||||
var converter = new PdfToImageConverter();
|
||||
return converter.ConvertToBitmap(pdfPage);
|
||||
}
|
||||
|
||||
public static IEnumerable<Stream> ConvertToJpgStreams(this PdfDocument pdfDocument)
|
||||
{
|
||||
var converter = new PdfToImageConverter();
|
||||
return converter.ConvertToJpgStreams(pdfDocument);
|
||||
}
|
||||
|
||||
public static Stream ConvertPageToJpg(this PdfPage pdfPage)
|
||||
{
|
||||
var converter = new PdfToImageConverter();
|
||||
return converter.ConvertToJpgStream(pdfPage);
|
||||
}
|
||||
|
||||
internal static FontStyle GetFontStyle(this FontNames fontNames)
|
||||
{
|
||||
var fontname = fontNames.GetFontName();
|
||||
var fontStyleRegex = Regex.Match(fontname, @"[-,][\w\s]+$");
|
||||
|
||||
if (fontStyleRegex.Success)
|
||||
{
|
||||
var result = fontStyleRegex.Value.ToLower();
|
||||
if (result.Contains("bold"))
|
||||
{
|
||||
return FontStyle.Bold;
|
||||
}
|
||||
}
|
||||
|
||||
return FontStyle.Regular;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace itext.pdfimage.Models
|
||||
{
|
||||
public interface IChunk
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace itext.pdfimage.Models
|
||||
{
|
||||
public class ImageChunk : IChunk
|
||||
{
|
||||
public float X { get; set; }
|
||||
public float Y { get; set; }
|
||||
public float W { get; set; }
|
||||
public float H { get; set; }
|
||||
public Image Image { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
using iText.Kernel.Pdf.Canvas.Parser.Data;
|
||||
using itext.pdfimage.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using iText.Kernel.Pdf.Xobject;
|
||||
using System.Drawing.Imaging;
|
||||
|
||||
namespace iText.Kernel.Pdf.Canvas.Parser.Listener
|
||||
{
|
||||
public class ImageListener : FilteredEventListener
|
||||
{
|
||||
private readonly SortedDictionary<float, IChunk> chunkDictionairy;
|
||||
private Func<float> increaseCounter;
|
||||
|
||||
public ImageListener(SortedDictionary<float, IChunk> chunkDictionairy, Func<float> increaseCounter)
|
||||
{
|
||||
this.chunkDictionairy = chunkDictionairy;
|
||||
this.increaseCounter = increaseCounter;
|
||||
}
|
||||
|
||||
public override void EventOccurred(IEventData data, EventType type)
|
||||
{
|
||||
if (type != EventType.RENDER_IMAGE) return;
|
||||
|
||||
float counter = increaseCounter();
|
||||
|
||||
var renderInfo = (ImageRenderInfo)data;
|
||||
|
||||
var imageObject = renderInfo.GetImage();
|
||||
Bitmap image;
|
||||
|
||||
try
|
||||
{
|
||||
var imageBytes = imageObject.GetImageBytes();
|
||||
image = new Bitmap(new MemoryStream(imageBytes));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var smask = imageObject.GetPdfObject().GetAsStream(PdfName.SMask);
|
||||
|
||||
if (smask != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var maskImageObject = new PdfImageXObject(smask);
|
||||
var maskBytes = maskImageObject.GetImageBytes();
|
||||
using (var maskImage = new Bitmap(new MemoryStream(maskBytes)))
|
||||
{
|
||||
image = GenerateMaskedImage(image, maskImage);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
var matix = renderInfo.GetImageCtm();
|
||||
|
||||
var imageChunk = new ImageChunk
|
||||
{
|
||||
X = matix.Get(Geom.Matrix.I31),
|
||||
Y = matix.Get(Geom.Matrix.I32),
|
||||
W = matix.Get(Geom.Matrix.I11),
|
||||
H = matix.Get(Geom.Matrix.I22),
|
||||
Image = image
|
||||
};
|
||||
|
||||
chunkDictionairy.Add(counter, imageChunk);
|
||||
|
||||
base.EventOccurred(data, type);
|
||||
}
|
||||
|
||||
private Bitmap GenerateMaskedImage(Bitmap image, Bitmap mask)
|
||||
{
|
||||
var output = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppArgb);
|
||||
var rect = new Rectangle(0, 0, image.Width, image.Height);
|
||||
var bitsMask = mask.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
|
||||
var bitsInput = image.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
|
||||
var bitsOutput = output.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||
|
||||
unsafe
|
||||
{
|
||||
for (int y = 0; y < image.Height; y++)
|
||||
{
|
||||
byte* ptrMask = (byte*)bitsMask.Scan0 + y * bitsMask.Stride;
|
||||
byte* ptrInput = (byte*)bitsInput.Scan0 + y * bitsInput.Stride;
|
||||
byte* ptrOutput = (byte*)bitsOutput.Scan0 + y * bitsOutput.Stride;
|
||||
for (int x = 0; x < image.Width; x++)
|
||||
{
|
||||
ptrOutput[4 * x] = ptrInput[4 * x]; // blue
|
||||
ptrOutput[4 * x + 1] = ptrInput[4 * x + 1]; // green
|
||||
ptrOutput[4 * x + 2] = ptrInput[4 * x + 2]; // red
|
||||
ptrOutput[4 * x + 3] = ptrMask[4 * x]; // alpha
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mask.UnlockBits(bitsMask);
|
||||
image.UnlockBits(bitsInput);
|
||||
output.UnlockBits(bitsOutput);
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace itext.pdfimage.Extensions
|
||||
{
|
||||
public static class MeasuringExtensions
|
||||
{
|
||||
//72 points == 1
|
||||
//72px x 72px is 1inch x 1inch at a 72dpi resolution
|
||||
|
||||
public static int Dpi { get; set; } = 300;
|
||||
|
||||
public static float PixelsToPoints(this float value, int? dpi = null)
|
||||
{
|
||||
return value / (dpi ?? Dpi) * 72;
|
||||
}
|
||||
|
||||
public static int PointsToPixels(this int value, int? dpi = null)
|
||||
{
|
||||
return PointsToPixels((float)value);
|
||||
}
|
||||
|
||||
public static int PointsToPixels(this float value, int? dpi = null)
|
||||
{
|
||||
return (int)(value * (dpi ?? Dpi) / 72);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
using iText.Kernel.Pdf;
|
||||
using iText.Kernel.Pdf.Canvas.Parser;
|
||||
using iText.Kernel.Pdf.Canvas.Parser.Listener;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using itext.pdfimage.Models;
|
||||
using itext.pdfimage.Extensions;
|
||||
using System.Threading;
|
||||
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Drawing.Text;
|
||||
|
||||
|
||||
namespace itext.pdfimage
|
||||
{
|
||||
public class PdfToImageConverter
|
||||
{
|
||||
private static int counter;
|
||||
|
||||
public IEnumerable<Bitmap> ConvertToBitmaps(PdfDocument pdfDocument)
|
||||
{
|
||||
counter = 0;
|
||||
|
||||
var numberOfPages = pdfDocument.GetNumberOfPages();
|
||||
|
||||
for (var i = 1; i <= numberOfPages; i++)
|
||||
{
|
||||
var currentPage = pdfDocument.GetPage(i);
|
||||
|
||||
yield return ConvertToBitmap(currentPage);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Stream> ConvertToJpgStreams(PdfDocument pdfDocument)
|
||||
{
|
||||
foreach (var bmp in ConvertToBitmaps(pdfDocument))
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
bmp.Save(ms, ImageFormat.Jpeg);
|
||||
yield return ms;
|
||||
}
|
||||
bmp.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Bitmap ConvertToBitmap(PdfPage pdfPage)
|
||||
{
|
||||
var rotation = pdfPage.GetRotation();
|
||||
|
||||
var chunkDictionairy = new SortedDictionary<float, IChunk>();
|
||||
|
||||
FilteredEventListener listener = new FilteredEventListener();
|
||||
listener.AttachEventListener(new TextListener(chunkDictionairy, IncreaseCounter));
|
||||
listener.AttachEventListener(new ImageListener(chunkDictionairy, IncreaseCounter));
|
||||
PdfCanvasProcessor processor = new PdfCanvasProcessor(listener);
|
||||
processor.ProcessPageContent(pdfPage);
|
||||
|
||||
//var size = currentPage.GetPageSizeWithRotation();
|
||||
var size = pdfPage.GetPageSize();
|
||||
|
||||
var width = size.GetWidth().PointsToPixels();
|
||||
var height = size.GetHeight().PointsToPixels();
|
||||
|
||||
Bitmap bmp = new Bitmap(width, height);
|
||||
using (Graphics g = Graphics.FromImage(bmp))
|
||||
{
|
||||
g.FillRectangle(Brushes.White, 0, 0, bmp.Width, bmp.Height);
|
||||
|
||||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
g.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;
|
||||
|
||||
foreach (var chunk in chunkDictionairy)
|
||||
{
|
||||
g.ResetTransform();
|
||||
|
||||
g.RotateTransform(-rotation);
|
||||
|
||||
if (chunk.Value is Models.ImageChunk imageChunk)
|
||||
{
|
||||
var imgW = imageChunk.W.PointsToPixels();
|
||||
var imgH = imageChunk.H.PointsToPixels();
|
||||
var imgX = imageChunk.X.PointsToPixels();
|
||||
var imgY = (size.GetHeight() - imageChunk.Y - imageChunk.H).PointsToPixels();
|
||||
|
||||
g.TranslateTransform(imgX, imgY, MatrixOrder.Append);
|
||||
g.DrawImage(imageChunk.Image, 0, 0, imgW, imgH);
|
||||
imageChunk.Image.Dispose();
|
||||
}
|
||||
else if (chunk.Value is Models.TextChunk textChunk)
|
||||
{
|
||||
var chunkX = textChunk.Rect.GetX().PointsToPixels();
|
||||
var chunkY = bmp.Height - textChunk.Rect.GetY().PointsToPixels();
|
||||
|
||||
var fontSize = textChunk.FontSize.PointsToPixels();
|
||||
|
||||
Font font;
|
||||
try
|
||||
{
|
||||
font = new Font(textChunk.FontFamily, fontSize, textChunk.FontStyle, GraphicsUnit.Pixel);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//log error
|
||||
|
||||
font = new Font("Calibri", 11, textChunk.FontStyle, GraphicsUnit.Pixel);
|
||||
}
|
||||
|
||||
g.TranslateTransform(chunkX, chunkY, MatrixOrder.Append);
|
||||
|
||||
//g.DrawString(textChunk.Text, font, new SolidBrush(textChunk.Color), chunkX, chunkY);
|
||||
g.DrawString(textChunk.Text, font, new SolidBrush(textChunk.Color), 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
g.Flush();
|
||||
}
|
||||
|
||||
return bmp;
|
||||
}
|
||||
|
||||
public Stream ConvertToJpgStream(PdfPage pdfPage)
|
||||
{
|
||||
var bmp = ConvertToBitmap(pdfPage);
|
||||
var ms = new MemoryStream();
|
||||
|
||||
bmp.Save(ms, ImageFormat.Jpeg);
|
||||
bmp.Dispose();
|
||||
return ms;
|
||||
|
||||
}
|
||||
|
||||
private Func<float> IncreaseCounter = () => counter = Interlocked.Increment(ref counter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Drawing;
|
||||
|
||||
|
||||
namespace itext.pdfimage.Models
|
||||
{
|
||||
public class TextChunk : IChunk
|
||||
{
|
||||
public string Text { get; set; }
|
||||
public iText.Kernel.Geom.Rectangle Rect { get; set; }
|
||||
public string FontFamily { get; set; }
|
||||
public int FontSize { get; set; }
|
||||
public FontStyle FontStyle { get; set; }
|
||||
public float SpaceWidth { get; set; }
|
||||
public Color Color { get; internal set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
using iText.Kernel.Geom;
|
||||
using iText.Kernel.Pdf.Canvas.Parser.Data;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using itext.pdfimage.Extensions;
|
||||
using itext.pdfimage.Models;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace iText.Kernel.Pdf.Canvas.Parser.Listener
|
||||
{
|
||||
public class TextListener : LocationTextExtractionStrategy
|
||||
{
|
||||
private readonly SortedDictionary<float, IChunk> chunkDictionairy;
|
||||
private Func<float> increaseCounter;
|
||||
|
||||
public TextListener(SortedDictionary<float, IChunk> chunkDictionairy, Func<float> increaseCounter)
|
||||
{
|
||||
this.chunkDictionairy = chunkDictionairy;
|
||||
this.increaseCounter = increaseCounter;
|
||||
}
|
||||
|
||||
public override void EventOccurred(IEventData data, EventType type)
|
||||
{
|
||||
return;
|
||||
|
||||
if (!type.Equals(EventType.RENDER_TEXT)) return;
|
||||
|
||||
TextRenderInfo renderInfo = (TextRenderInfo)data;
|
||||
|
||||
float counter = increaseCounter();
|
||||
|
||||
var font = renderInfo.GetFont().GetFontProgram();
|
||||
var originalFontName = font.ToString();
|
||||
var fontRegex = Regex.Match(originalFontName, @"(?<=\+)[a-zA-Z\s]+");
|
||||
|
||||
string fontName = fontRegex.Success ? fontRegex.Value : originalFontName;
|
||||
|
||||
var fontStyle = font.GetFontNames().GetFontStyle();
|
||||
|
||||
float curFontSize = renderInfo.GetFontSize();
|
||||
|
||||
float key = counter;
|
||||
|
||||
IList<TextRenderInfo> text = renderInfo.GetCharacterRenderInfos();
|
||||
foreach (TextRenderInfo character in text)
|
||||
{
|
||||
key += 0.001f;
|
||||
|
||||
var textRenderMode = character.GetTextRenderMode();
|
||||
var opacity = character.GetGraphicsState().GetFillOpacity();
|
||||
|
||||
//if (textRenderMode != 0 || opacity != 1)
|
||||
//{
|
||||
|
||||
//}
|
||||
|
||||
string letter = character.GetText();
|
||||
|
||||
Color color;
|
||||
|
||||
var fillColor = character.GetFillColor();
|
||||
var colors = fillColor.GetColorValue();
|
||||
if(colors.Length == 1)
|
||||
{
|
||||
color = Color.FromArgb((int)(255 * (1 - colors[0])), Color.Black);
|
||||
}
|
||||
else if (colors.Length == 3)
|
||||
{
|
||||
color = Color.FromArgb((int)(255 * colors[0]), (int)(255 * colors[1]), (int)(255 * colors[2]));
|
||||
}
|
||||
else if (colors.Length == 4)
|
||||
{
|
||||
color = Color.FromArgb((int)(255 * colors[0]), (int)(255 * colors[1]), (int)(255 * colors[2]), (int)(255 * colors[3]));
|
||||
}
|
||||
else
|
||||
{
|
||||
color = Color.Black;
|
||||
}
|
||||
|
||||
//if(letter == "A")
|
||||
//{
|
||||
|
||||
//}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(letter)) continue;
|
||||
|
||||
//Get the bounding box for the chunk of text
|
||||
var bottomLeft = character.GetDescentLine().GetStartPoint();
|
||||
var topRight = character.GetAscentLine().GetEndPoint();
|
||||
|
||||
//Create a rectangle from it
|
||||
var rect = new Geom.Rectangle(
|
||||
bottomLeft.Get(Vector.I1),
|
||||
topRight.Get(Vector.I2),
|
||||
topRight.Get(Vector.I1),
|
||||
topRight.Get(Vector.I2)
|
||||
);
|
||||
|
||||
var currentChunk = new itext.pdfimage.Models.TextChunk()
|
||||
{
|
||||
Text = letter,
|
||||
Rect = rect,
|
||||
FontFamily = fontName,
|
||||
FontSize = (int)curFontSize,
|
||||
FontStyle = fontStyle,
|
||||
Color = color,
|
||||
SpaceWidth = character.GetSingleSpaceWidth() / 2f
|
||||
};
|
||||
|
||||
chunkDictionairy.Add(key, currentChunk);
|
||||
}
|
||||
|
||||
base.EventOccurred(data, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user