因业务需要把pdf中的logo图片换成其它图片.
百度了很久也没找到相关的解决方案. 后来总算是解决了.
总结起来一句话, itextsharp的文档太少了. 尤其是关于几个基础类的相关文档, 还有pdf文档格式也是很要命的难理解. 简直是狗屎般的格式. 用Sprie.pdf 很好用很简单, 就是得花钱.
业务需求还需要合并多个pdf, 搞了好久实在是不知道如何合并的同时再去掉logo.
就改成分两步走. 先合并成一个文件后, 输出到MemoryStream,然后再去掉logo…
废话不多说上代码.
private void Main()
{
//读取待替换logo的pdf文件, 这个地方可以是Stream可以放在内存里.
PdfReader reader2 = new PdfReader("D:\\a.pdf");
//输出文件流.
using (FileStream fs = new FileStream("D:\\Marge.pdf", FileMode.Create, FileAccess.Write, FileShare.None))
{
using (PdfStamper stamper = new PdfStamper(reader2, fs))
{
PdfDictionary page;
//Get the page count
int pageCount2 = reader2.NumberOfPages;
//Loop through each page
for (int i = 1; i <= pageCount2; i++)
{
//Get the page
page = reader2.GetPageN(i);
PdfObject obj = FindImageInPDFDictionary(page, DistinguishImageIsLogo);
//Get the raw content
//contentarray = page.GetAsArray(PdfName.RESOURCES);
if (obj != null)
{
PdfReader.KillIndirect(obj);//移除老图片,只是移除了关联.
iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance("D:\\cc.png"); //必须每个页面新建一个图片的对象,否则会只在第一个页面有图片.
iTextSharp.text.Image maskImage = img.ImageMask;
if (maskImage != null)
{
stamper.Writer.AddDirectImageSimple(maskImage); //把新图片写进去
stamper.Writer.AddDirectImageSimple(img, (PRIndirectReference)obj); //把新图片写进去.
}
}
}
}
}
}
//在pdf页面中 找到logo图片
private static PdfObject FindImageInPDFDictionary(PdfDictionary pg, DistinguishImage distinguishMethod)
{
PdfDictionary res = (PdfDictionary)PdfReader.GetPdfObject(pg.Get(PdfName.RESOURCES));
PdfDictionary xobj = (PdfDictionary)PdfReader.GetPdfObject(res.Get(PdfName.XOBJECT));
if (xobj != null)
{
foreach (PdfName name in xobj.Keys)
{
Console.WriteLine(name.ToString());
PdfObject obj = xobj.Get(name);
if (obj.IsIndirect())
{
PdfDictionary tg = (PdfDictionary)PdfReader.GetPdfObject(obj);
PdfName type = (PdfName)PdfReader.GetPdfObject(tg.Get(PdfName.SUBTYPE));
//image at the root of the pdf
if (PdfName.IMAGE.Equals(type))
{
if (distinguishMethod(tg) == true)
{
return obj;
}
else
{
continue;//继续找
}
}// image inside a form
else if (PdfName.FORM.Equals(type))
{
return FindImageInPDFDictionary(tg, distinguishMethod);
} //image inside a group
else if (PdfName.GROUP.Equals(type))
{
return FindImageInPDFDictionary(tg, distinguishMethod);
}
}
}
}
return null;
}
/// <summary>
/// 辨别图片的委托
/// </summary>
/// <param name="imgObject"></param>
/// <returns></returns>
delegate bool DistinguishImage(PdfDictionary imgObject);
/// <summary>
/// 辨别图片是不是LOGO
/// </summary>
/// <param name="imgObject"></param>
/// <returns></returns>
private static bool DistinguishImageIsLogo(PdfDictionary imgObject)
{
int width, height, length;
int.TryParse(imgObject.Get(PdfName.WIDTH).ToString(), out width);
int.TryParse(imgObject.Get(PdfName.HEIGHT).ToString(), out height);
int.TryParse(imgObject.Get(PdfName.LENGTH).ToString(), out length);
//从这3个参数就可以判断是不是logo, 也可以按照name来判断.还可以硬编码判断两个图片对象是否一样.
if (width == 270 && height == 111 && length == 11878)
{
return true;
}
else
{
return false;
}
}
以上是处理文件的方式,处理MemoryStream流, 需要用到特殊的自定义的内存流,防止程序自动关闭了流.
public class PdfMemoryStream : System.IO.MemoryStream
{
public PdfMemoryStream(byte[] bytes) :base(bytes)
{
AllowClose = true;
}
public PdfMemoryStream()
{
AllowClose = true;
}
public bool AllowClose { get; set; }
public override void Close()
{
if (AllowClose)
base.Close();
}
}
然后把Main方法重构了一下如下
/// <summary>
/// 替换PDF中的图片
/// </summary>
/// <param name="src">pdf文件流</param>
/// <param name="distinguishMethod">识别需要被替换图片的方法</param>
/// <param name="replaceToImg">替换成这个图片</param>
/// <returns></returns>
public static MemoryStream ReplaceImage(Stream src, DistinguishImage distinguishMethod, System.Drawing.Image replaceToImg)
{
PdfReader reader2 = new PdfReader(src);
PdfMemoryStream outMemoryStream = new PdfMemoryStream();
outMemoryStream.AllowClose = false;
using (PdfStamper stamper = new PdfStamper(reader2, outMemoryStream))
{
int pageCount2 = reader2.NumberOfPages;
for (int i = 1; i <= pageCount2; i++)
{
//Get the page
var page = reader2.GetPageN(i);
PdfObject obj = FindImageInPDFDictionary(page, distinguishMethod);
if (obj != null)
{
PdfReader.KillIndirect(obj);//移除老图片
iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(replaceToImg,BaseColor.WHITE,true);
iTextSharp.text.Image maskImage = img.ImageMask;
if (maskImage != null)
{
stamper.Writer.AddDirectImageSimple(maskImage);
stamper.Writer.AddDirectImageSimple(img, (PRIndirectReference)obj);
}
}
}
}
outMemoryStream.Position = 0;
return outMemoryStream;
}