Logo

Aspose、iText实现给PDF添加水印功能

photo

2022年07月18日

一、背景

最近有个项目需要实现给pdf、word、excel、图片等资源添加水印功能,最开始用的是前端来添加水印,后面发现会一点技术的还是可以通过请求捕获到你的源文件,那我加水印的意义何在?最后只能在后台直接返回已经处理过的文件,这样就不会有源文件被捕获的问题了。
好了,功能背景介绍完后,进入正题。
是怎么实现的呢?最开始想到的就是Aspose PDF.dll这个来做,因为项目中有其他地方用了Aspose,cell、Aspose,word。所以对于这个需求,第一时间想到还是Aspose。
下面就看看具体代码是怎么实现的吧。

二、实现功能

1、Aspose PDF.dll 实现PDF添加水印

/// <summary>
        /// pdf添加文字水印(自定义水印行数和列数)
        /// </summary>
        /// <param name="pdfStream">pdf文件流</param>
        /// <param name="watermarkText">水印文字</param>
        /// <param name="fontSize">文字大小</param>
        /// <param name="fontColor">文字颜色</param>
        /// <param name="typeface">字体:(中文水印请自行查找相应字体的英文名称,如:宋体(SimSun),黑体(SimHei),仿宋(FangSong),微软雅黑(Microsoft YaHei)等等...)</param>
        /// <param name="rotation">倾斜角度</param>
        /// <param name="opacity">不透明度(值为:0-1)</param>
        /// <param name="watermarkRowNum">水印行数</param>
        /// <param name="watermarkColumnNum">水印列数</param>
        /// <returns></returns>
        public MemoryStream AddTextWatermarkCustom(MemoryStream pdfStream, string watermarkText, float fontSize, Color fontColor, string typeface, double rotation = 0, double opacity = 0, int watermarkRowNum = 1, int watermarkColumnNum = 1)
        {
            try
            {
                /*计算倾斜高度-start*/
                var angle = Math.Abs(rotation);
                var width = watermarkText.Length * fontSize;
                var radian = 2 * Math.PI / 360 * angle;
                var top = Math.Sin(radian) * width;
                /*计算倾斜高度-end*/
                Document document = new Document(pdfStream);
                for (int i = 1; i <= document.Pages.Count; i++)
                {
                    var pageWidth = document.Pages[i].GetPageRect(false).Width;
                    var pageHeight = document.Pages[i].GetPageRect(false).Height;
                    WatermarkArtifact artifact = new WatermarkArtifact();
                    artifact.SetTextAndState(watermarkText, new TextState
                    {
                        FontSize = fontSize,
                        ForegroundColor = fontColor,
                        Font = FontRepository.FindFont(typeface),
                    });
                    artifact.Rotation = rotation;
                    artifact.Opacity = opacity;
                    artifact.IsBackground = false;
                    if (watermarkRowNum == 1 && watermarkColumnNum == 1)
                    {
                        artifact.ArtifactHorizontalAlignment = HorizontalAlignment.Center;
                        artifact.ArtifactVerticalAlignment = VerticalAlignment.Center;
                        document.Pages[i].Artifacts.Add(artifact);
                    }
                    else
                    {
                        artifact.ArtifactHorizontalAlignment = HorizontalAlignment.Left;
                        artifact.ArtifactVerticalAlignment = VerticalAlignment.Top;
                        for (int j = 1; j <= watermarkColumnNum; j++)
                        {
                            artifact.LeftMargin = (j - 1) * (pageWidth / watermarkColumnNum) + fontSize;
                            for (int z = 1; z <= watermarkRowNum; z++)
                            {
                                artifact.TopMargin = (z - 1) * (pageHeight / watermarkRowNum) + top;
                                document.Pages[i].Artifacts.Add(artifact);
                            }
                        }
                    }
                }
                var outStream = new MemoryStream();
                document.Save(outStream, SaveFormat.Pdf);
                outStream.Seek(0, SeekOrigin.Begin);
                return outStream;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

这个方法基本网上都有,我就直接贴上来了。
所以Aspose实现给PDF加水印还是比较方便的。
但是
但是
但是
经过简单的测试就发现一个巨大的问题。
在这里插入图片描述
抛异常了,翻译了一下:在评估模式下最多可以查看 4 个元素(对于任何集合)。
在这里插入图片描述
什么玩意?百度了一下,发现我们用的Aspose是试用版,试用版有限制,最多支持4页。wnm~
怎么办呢,正式版老贵了,咋办,找破解的呗。费了老大劲找了很多资源,最后,终于

我放弃了。。。。

在这里插入图片描述
含泪,没有办法,没有积分,C币,网上都要积分啥的才能下载。
至此Aspose实现给PDF添加水印的功能也就这样了。最起码学习了一下。

2、iTextSharp 实现PDF添加水印

经过上面的一系列打击之后,我决定请教微信群聊里面的众多大佬。
在这里插入图片描述
大佬的一句itext让我打开的新的大门。我立马流利的打开浏览器,百度输入itext,研究了一下发现真的可以,而且有很多写好的文章,于是我立马起来劲头,又是一波流利的ctrl c+v。根据自己的实际场景,略加修改,就有了一下代码:

/// <summary>
        /// 添加水印
        /// </summary>
        /// <param name="pdfStream"></param>
        /// <param name="waterMarkName"></param>
        /// <returns></returns>
        public MemoryStream SetWaterMark(Stream pdfStream, string waterMarkName)
        {
            try
            {
                MemoryStream outStream = new MemoryStream();
                PdfReader pdfReader = new PdfReader(pdfStream);
                PdfStamper pdfStamper = new PdfStamper(pdfReader, outStream);
                
                pdfStamper.Writer.CloseStream = false;
                int total = pdfReader.NumberOfPages + 1;
                iTextSharp.text.Rectangle psize = pdfReader.GetPageSize(1);
                float width = psize.Width;
                float height = psize.Height;
                PdfContentByte content;
                BaseFont font = BaseFont.CreateFont(@"C:\WINDOWS\Fonts\SIMFANG.TTF", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
                PdfGState gs = new PdfGState();
                for (int i = 1; i < total; i++)
                {
                    content = pdfStamper.GetOverContent(i);//在内容上方加水印
                                                           //content = pdfStamper.GetUnderContent(i);//在内容下方加水印
                                                           //透明度
                    gs.FillOpacity = 0.3f;
                    content.SetGState(gs);
                    //content.SetGrayFill(0.3f);
                    //开始写入文本
                    content.BeginText();
                    content.SetColorFill(BaseColor.GRAY);
                    content.SetFontAndSize(font, 30);
                    content.SetTextMatrix(0, 0);

                    for (int ii = 0; ii < 5; ii++)
                    {
                        for (int j = 0; j < 5; j++)
                        {
                            content.ShowTextAligned(Element.ALIGN_CENTER, waterMarkName, (50.5f + ii * 350),(40.0f + j * 150), -45);
                        }
                    }

                    content.EndText();
                }

                if (pdfStamper != null)
                    pdfStamper.Close();

                if (pdfReader != null)
                    pdfReader.Close();

                return outStream;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

看了一下代码,发现并没有什么难点。算是一眼就能看明白写的是啥,我略加修改添加了两个for循环来实现水印的重复,不然原来它就一页一个水印。
同学们如果不需要一个多个水印的,可以把for循环去掉哈。
代码借鉴完后,抱着激动的心,尝试了一下,我擦,可以,而且很快,2380多页的pdf,不到6s就导出了。可以可以。
看来微信群里面的大佬还是比较厉害的,主要自己太菜了。
在这里插入图片描述
哦哦,差点忘记了,调用我顺便实现了两种,提供给大家参考,虽然不算是什么牛逼的代码,但是可以省去大家写哎,ctrl c+v 不香么~~

第一种:网络地址的pdf文件加水印后直接浏览器下载

/// <summary>
/// 下载水印PDF
/// </summary>
/// <param name="fileUrl">pdf地址</param>
/// <param name="waterMarkText">水印文字</param>
/// <param name="outFileName">输出文件名</param>
void GetWatermark(string fileUrl, string waterMarkText, string outFileName)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(fileUrl);
            request.Method = "GET";
            WebResponse response = request.GetResponse();
            var stream = response.GetResponseStream();

            var outStream = SetWaterMark(stream, waterMarkText);
            var fileBytes = outStream.ToArray();

            httpContext.Response.ContentType = "application/octet-stream";
            httpContext.Response.AppendHeader("Content-Disposition", "attachment;filename=" + outFileName);//文件名和格式(格式可以自己定)
            httpContext.Response.AddHeader("Content-Length", fileBytes.Length.ToString());//文件大小
            httpContext.Response.BinaryWrite(fileBytes); //放入byte[]
            httpContext.Response.End();
            httpContext.Response.Close();
        }

第二种:给本地pdf文件添加水印

/// <summary>
        /// 给本地PDF文件添加水印
        /// </summary>
        /// <param name="filePath">E:/studyHome/my_tools_project/tools_project/123.pdf</param>
        /// <param name="text">xxx</param>
        public void AddWaterMark(string filePath, string text)
        {
            PdfReader pdfReader = null;
            PdfStamper pdfStamper = null;
            string tempPath = Path.GetDirectoryName(filePath) + Path.GetFileNameWithoutExtension(filePath) + "_temp.pdf";

            try
            {
                pdfReader = new PdfReader(filePath);
                pdfStamper = new PdfStamper(pdfReader, new FileStream(tempPath, FileMode.Create));
                int total = pdfReader.NumberOfPages + 1;
                iTextSharp.text.Rectangle psize = pdfReader.GetPageSize(1);
                float width = psize.Width;
                float height = psize.Height;
                PdfContentByte content;
                BaseFont font = BaseFont.CreateFont(@"C:\WINDOWS\Fonts\SIMFANG.TTF", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
                PdfGState gs = new PdfGState();
                for (int i = 1; i < total; i++)
                {
                    content = pdfStamper.GetOverContent(i);//在内容上方加水印
                                                           //content = pdfStamper.GetUnderContent(i);//在内容下方加水印
                                                           //透明度
                    gs.FillOpacity = 0.3f;
                    content.SetGState(gs);
                    //content.SetGrayFill(0.3f);
                    //开始写入文本
                    content.BeginText();
                    content.SetColorFill(BaseColor.GRAY);
                    content.SetFontAndSize(font, 30);
                    content.SetTextMatrix(0, 0);
                    content.ShowTextAligned(Element.ALIGN_CENTER, text, width - 120, height - 120, -45);

                    content.EndText();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (pdfStamper != null)
                    pdfStamper.Close();

                if (pdfReader != null)
                    pdfReader.Close();
                File.Copy(tempPath, filePath, true);
                File.Delete(tempPath);
            }
        }

好了,至此,这个功能点算是解决了,我呢也学习了不少东西
发出来给大家做个参考,也是给自己一个记录。

欢迎大家一起探讨学习。

差点忘记,给大家看看效果:
在这里插入图片描述

本文为原创文章,请注意保留出处!

热门文章

修复群晖Synology Drive client右键菜单缺失问题 本教程主要解决windows10右键菜单中没有SynologyDrive菜单的问题,整体思路是找到...修复群晖SynologyDriveclient右键菜单缺失问题 作者:Pastore Antonio
1827 浏览量
docker如何查看一个镜像内部的目录结构及其内部都有哪些文件 前言:有时候我们会在docker上下载一个镜像,或者是上传一个镜像到docker上,甚至有时候就是在...docker如何查看一个镜像内部的目录结构及其内部都有哪些文件 作者:Pastore Antonio
1808 浏览量
Adobe Acrobat Pro 激活 这里记录了一些AdobeAcrobat的激活教程和组件。浏览量:1,688 作者:Pastore Antonio
1535 浏览量
configure: error: Package requirements (oniguruma) were not met configure:error:Packagerequirements(oniguruma)...configure:error:Packagerequirements(oniguruma)werenotmet 作者:Pastore Antonio
1535 浏览量
追寻日出,找回自己 为什么我要去追寻日出?其实我是一个很懒的人,每次都起不来,直到有一次我在租房中睡到了大天亮,阳光照...追寻日出,找回自己 作者:Pastore Antonio
1515 浏览量