坐标系转换和PDF内嵌二维码

需求描述:在PDF文件上的某个指定位置显示指定内容的二维码,其中,位置为PDF的四个角(LU , RU, RD, LD) ;坐标为相对这四个角的坐标,即LU(50,50)指二维码的左上角相对PDF左上角的偏移(50,50); RD(50,50)即二维码的右下角相对PDF文件右下角的偏移(50,50),以此类推。包括二维码内容都由用户在页面上配置。在用itextsharp显示二维码的时候,需要的是二维码左下角相对PDF左下角的偏移,所以需要写一个坐标变换把用户的配置信息统一转换到itextsharp显示需要的坐标系下。
定义LD为基准坐标系,可用 ( 1 0 0 1 ) \begin{pmatrix}1 & 0 \\ 0 & 1 \\ \end{pmatrix} (1001)表示,则RD为 ( 1 0 0 − 1 ) \begin{pmatrix}1 & 0 \\ 0 & -1 \\ \end{pmatrix} (1001),RU为 ( − 1 0 0 − 1 ) \begin{pmatrix}-1 & 0 \\ 0 & -1 \\ \end{pmatrix} (1001),LU为 ( − 1 0 0 1 ) \begin{pmatrix}-1 & 0 \\ 0 & 1 \\ \end{pmatrix} (1001),设给出的坐标是LU(pos(x,y)),则有 pos’ = pos * matrix[‘LU’],然后加上LD坐标系下的PDF尺寸就是LD下的坐标啦,还要变换成二维码的左下角坐标,再加上二维码的尺寸搞定。代码里写了好多注释就怕自己搞晕了,哈哈!(顺便感谢何燕杰这篇博客里的c#矩阵运算类库)
唯一的缺陷就是会生成一个新的PDF文件,暂时不影响项目就没多在这里浪费时间了,如果有人知道怎么可以不需要频繁的delete、rename或moveto操作下不生成新的PDF文件而完成任务请告知,谢了先!主要还是对itextsharp不熟吧。
==我是找不到对象就自己new的分割线

    public class PdfQRHelper
    {
        #region 坐标系变换
        public enum BasePointName { LU = 0, RU, RD, LD };
        private static readonly Dictionary<BasePointName, Matrix> CoordinateBasis = new Dictionary<BasePointName, Matrix>()
        {
            {BasePointName.LD, new double[,] { {1, 0}, {0, 1} } },
            {BasePointName.RD, new double[,] { {-1, 0},{0, 1} } },
            {BasePointName.LU, new double[,] { {1, 0}, {0, -1} } },
            {BasePointName.RU, new double[,] { {-1, 0}, {0, -1} } }
        };

        //变换到目标坐标系下后的数值修正系数矩阵,与BasePointName顺序一一对应
        private static readonly Matrix SizeFixMatrix = new double[,] 
        {
            {0, 1},
            {1, 1},
            {1, 0},
            {0, 0}
        };

        /// <summary>
        /// 坐标变换,将原正交基定义下的坐标变换到[[1,0],[0,1]]定义的坐标下
        /// </summary>
        /// <param name="srcBasisName">原始正交基</param>
        /// <param name="srcPosition">原始正交基下坐标</param>
        /// <param name="destBasisSize">目标坐标系的[w,h], 行向量</param>
        /// <returns>返回[[1,0],[0,1]]正交基下的坐标</returns>
        public static Vector CoordinateConvert(BasePointName srcBasisName, Vector srcPosition, Vector destBasisSize)
        {
            Matrix sizeFixMat = Dot(destBasisSize, SizeFixMatrix);
            Matrix srcPosMat = Matrix.Create(1, srcPosition.Count, srcPosition.Elements);            
            Vector resVec = new Vector((srcPosMat * CoordinateBasis[srcBasisName]).GetRow(0).ToArray(), VectorType.Row);
            Vector fixVec = new Vector(sizeFixMat.GetRow((int)srcBasisName).ToArray(), VectorType.Row);
            return resVec + fixVec;

        }

        /// <summary>
        /// 向量和矩阵做点乘
        /// </summary>
        /// <param name="vec">运算的向量,行向量</param>
        /// <param name="matrix">运算的矩阵</param>
        /// <returns>返回点乘后的矩阵</returns>
        private static Matrix Dot(Vector vec, Matrix matrix)
        {
            Matrix mat = new Matrix(matrix);
            if(vec.Count != matrix.ColumnCount)
            {
                throw new Exception("the colum must be equal!");                
            }

            for(int i = 0; i < matrix.RowCount; i++)
            {
                for (int j = 0; j<matrix.ColumnCount;j++)
                {
                    mat[i, j] = vec[j] * matrix[i, j];
                }
            }

            return mat;
        }
        #endregion

        #region 生成二维码
        /// <summary>
        /// 将相对四个角的坐标统一换算成二维码右下角的坐标,坐标系转换前调用
        /// </summary>
        /// <param name="basePoint">基坐标系</param>
        /// <param name="relativePos">相对四个角的坐标</param>
        /// <param name="qrSize">二维码大小</param>
        /// <returns>返回二维码右下角坐标</returns>
        private static double[] GetQRanderPos(PdfQRHelper.BasePointName basePoint, double[] relativePos, double[] qrSize)
        {
            double qrW = qrSize[0];
            double qrH = qrSize[1];
            double[] absPos = relativePos;
            switch (basePoint)
            {
                case PdfQRHelper.BasePointName.LU:
                    absPos[1] += qrSize[1];
                    break;
                case PdfQRHelper.BasePointName.RU:
                    absPos[0] += qrSize[0];
                    absPos[1] += qrSize[1];
                    break;
                case PdfQRHelper.BasePointName.RD:
                    absPos[0] += qrSize[0];
                    break;
            }
            return absPos;
        }

        /// <summary>
        /// 生成二维码
        /// </summary>
        /// <param name="content">需要生成二维码的内容</param>
        /// <param name="size">二维码图片长宽大小</param>
        /// <returns></returns>
        public static Bitmap CreateQRbmp(string content, int size)
        {
            try
            {
                var options = new QrCodeEncodingOptions
                {
                    DisableECI = true,
                    CharacterSet = "UTF-8",
                    Width = size,
                    Height = size,
                    Margin = 0,
                    ErrorCorrection = ZXing.QrCode.Internal.ErrorCorrectionLevel.H
                };
                var writer = new BarcodeWriter();
                writer.Format = BarcodeFormat.QR_CODE;
                writer.Options = options;
                var bmp = writer.Write(content);
                return bmp;
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        private static double[] GetQRPosition(PdfQRHelper.BasePointName basePoint, double[] srcPos,float[] pdfSize)
        {            
            float height = pdfSize[1];
            float width = pdfSize[0]; 
            Vector srcSize = new Vector(new double[] { width, height }, VectorType.Row);
            Vector srcPosition = new Vector(srcPos, VectorType.Row);
            Vector pos = CoordinateConvert(basePoint, srcPosition, srcSize);            
            return pos.Elements;
        }

        /// <summary>
        /// 在原PDF文件上的指定位置添加指定内容的二维码
        /// </summary>
        /// <param name="srcPdfPath">原PDF文件</param>
        /// <param name="qrContent">二维码内容</param>
        /// <param name="qrSize">二维码大小,单位:同PDF尺寸单位</param>
        /// <param name="qrRelPos">二维码相对四个角的位置,double[]类型</param>
        /// <param name="basePointName">PDF四个角枚举值,默认左下</param>
        /// <returns>在同一目录下返回带二维码的新PDF文件,文件名:原始文件名_qr.pdf,失败返回null</returns>
        public static string GenerateQRInPdf(string srcPdfPath,string qrContent, int qrSize,double[] qrRelPos,PdfQRHelper.BasePointName basePointName = BasePointName.LD)
        {
            string tempPdf = string.Format("{0}_qr.pdf", System.IO.Path.GetFileNameWithoutExtension(srcPdfPath));
            PdfReader pdfReader = null;
            PdfStamper stamper = null;
            try
            {
                pdfReader = new PdfReader(srcPdfPath);
                iTextSharp.text.Rectangle pageSize = pdfReader.GetPageSize(1);                
                stamper = new PdfStamper(pdfReader,new System.IO.FileStream(tempPdf, System.IO.FileMode.OpenOrCreate));
                PdfContentByte canvas = stamper.GetOverContent(1);
                string url = qrContent;                
                var qrImg = CreateQRbmp(url, qrSize);
                iTextSharp.text.Image image = iTextSharp.text.Image.GetInstance(qrImg, System.Drawing.Imaging.ImageFormat.Bmp);
                double[] absPos = GetQRanderPos(basePointName, qrRelPos, new double[] { qrSize, qrSize });
                double[] desPos = GetQRPosition(basePointName, absPos, new float[] { pageSize.Width, pageSize.Height });
                image.SetAbsolutePosition((float)desPos[0], (float)desPos[1]); // 添加二维码图片位置
                canvas.AddImage(image);                
            }
            catch (Exception ex)
            {
                return null;
            }
            finally
            {
                if (stamper != null)
                {
                    stamper.Close();
                    stamper = null;
                }
                if (pdfReader != null)
                {
                    pdfReader.Close();
                    pdfReader = null;
                }                              
            }
            return tempPdf;
        }        
        #endregion
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值