1. 程式人生 > >座標系轉換和PDF內嵌二維碼

座標系轉換和PDF內嵌二維碼

需求描述:在PDF檔案上的某個指定位置顯示指定內容的二維碼,其中,位置為PDF的四個角(LU , RU, RD, LD) ;座標為相對這四個角的座標,即LU(50,50)指二維碼的左上角相對PDF左上角的偏移(50,50); RD(50,50)即二維碼的右下角相對PDF檔案右下角的偏移(50,50),以此類推。包括二維碼內容都由使用者在頁面上配置。在用itextsharp顯示二維碼的時候,需要的是二維碼左下角相對PDF左下角的偏移,所以需要寫一個座標變換把使用者的配置資訊統一轉換到itextsharp顯示需要的座標系下。
定義LD為基準座標系,可用 (1001)表示,則RD為(1001),RU為

(1001),LU為(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 }