座標系轉換和PDF內嵌二維碼
阿新 • • 發佈:2019-02-06
需求描述:在PDF檔案上的某個指定位置顯示指定內容的二維碼,其中,位置為PDF的四個角(LU , RU, RD, LD) ;座標為相對這四個角的座標,即LU(50,50)指二維碼的左上角相對PDF左上角的偏移(50,50); RD(50,50)即二維碼的右下角相對PDF檔案右下角的偏移(50,50),以此類推。包括二維碼內容都由使用者在頁面上配置。在用itextsharp顯示二維碼的時候,需要的是二維碼左下角相對PDF左下角的偏移,所以需要寫一個座標變換把使用者的配置資訊統一轉換到itextsharp顯示需要的座標系下。
定義LD為基準座標系,可用 表示,則RD為,RU為 ,LU為,設給出的座標是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
}