需求描述:在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}
(100−1),RU为
(
−
1
0
0
−
1
)
\begin{pmatrix}-1 & 0 \\ 0 & -1 \\ \end{pmatrix}
(−100−1),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
}