.net通过iTextSharp.pdf操作pdf文件实现查找关键字签字盖章
lzj007 人气:0之前这个事情都CA公司去做的,现在给客户做demo,要模拟一下签字盖章了,我们的业务PDF文件是动态生成的所以没法通过坐标定位,只能通过关键字查找定位了。
之前在网上看了许多通多通过查询关键字,然后图片盖章的文章都不完整,说白了基本上没完成。我这边利用了网上查找关键字的方法。
我自己查看了相关Api,然后完善了这个功能。不多说了,直接上代码。
我这个只是示例,默认只取第一个关键字,多个相同关键字,根据业务场景定。 推荐方式:设置白色文字作为关键字,公司的业务我基本上都这样操作。
1.帮助类方法(关键字签字)
1 /// <summary> 2 /// pdf上图片签章 3 /// </summary> 4 public class SealPictureHelper 5 { 6 static float ReSizeMaxWidth = 30; 7 static float ReSizeMaxHeight = 30; 8 /// <summary> 9 /// 手写签字(流和base64格式) 10 /// </summary> 11 /// <param name="bytePdf">byte数组的pdf文件</param> 12 /// <param name="SignImgBase64">base64格式的图片</param> 13 /// <param name="SignKeyWord">关键字</param> 14 /// <returns></returns> 15 public static byte[] SignBase64Img(byte[] bytePdf, string SignImgBase64, string SignKeyWord) 16 { 17 byte[] newbytefile; 18 try 19 { 20 using (MemoryStream ms = new MemoryStream()) 21 { 22 // 创建一个PdfReader对象 23 using (PdfReader reader = new PdfReader(bytePdf)) 24 { 25 using (PdfStamper stamper = new PdfStamper(reader, ms)) 26 { 27 // 获得文档页数 28 int n = reader.NumberOfPages; 29 for (int i = 1; i <= n; i++) 30 { 31 //获取当前页 32 PdfContentByte cb = stamper.GetOverContent(i); 33 PdfLocation pz = new PdfLocation(); 34 iTextSharp.text.pdf.parser.PdfReaderContentParser p = new iTextSharp.text.pdf.parser.PdfReaderContentParser(reader); 35 p.ProcessContent<PdfLocation>(i, pz); 36 //查找当前页的关键字 37 pz.SearchKeywords(SignKeyWord); 38 if (pz.TextLocationInfo.Count > 0) 39 { 40 //坐标是从左下角往上,左下角为(0,0)零点 41 XTextInfo o = pz.TextLocationInfo[0]; 42 //获取关键字左上角开始坐标 43 string[] L_T_Location = o.TopLeft.ToString().Split(',');//272.15,766.46,1 44 var left_x = float.Parse(L_T_Location[0]); 45 var top_y = float.Parse(L_T_Location[1]); 46 //获取关键字右下角结束坐标 47 string[] R_B_Location = o.BottomRight.ToString().Split(',');//305.15,755.46,1 48 var right_x = float.Parse(R_B_Location[0]); 49 var bottom_y = float.Parse(R_B_Location[1]); 50 //计算得到关键字的中心点 51 float x = (right_x - left_x) / 2 + left_x; 52 float y = (top_y - bottom_y) / 2 + bottom_y; 53 var imgtest = ConvertBase64ToImage(SignImgBase64); 54 //创建一个图片对象 55 iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(imgtest, System.Drawing.Imaging.ImageFormat.Jpeg); 56 //设置图片的指定大小 57 float expectWidth = img.Width; 58 float expectHeight = img.Height; 59 if (img.Width > img.Height && img.Width > ReSizeMaxWidth) 60 { 61 expectWidth = ReSizeMaxWidth; 62 expectHeight = expectWidth * img.Height / img.Width; 63 } 64 else if (img.Height > img.Width && img.Height > ReSizeMaxHeight) 65 { 66 expectHeight = ReSizeMaxHeight; 67 expectWidth = expectHeight * img.Width / img.Height; 68 } 69 img.ScaleToFit(expectWidth, expectHeight);//img.ScaleToFit(128, 128); 70 //设置图片位置在关键字正中心 71 img.SetAbsolutePosition(x - expectWidth / 2, y - expectHeight / 2);// 72 img.Transparency = new int[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 73 cb.AddImage(img); 74 } 75 } 76 } 77 } 78 newbytefile = ms.ToArray(); 79 } 80 return newbytefile; 81 } 82 catch (Exception ex) 83 { 84 South.Tools.Logger.SaveLogUtil.Error(ex.Message, ex, "SignPicPDF"); 85 return null; 86 } 87 } 88 /// <summary> 89 /// 手写签字(文件路径) 90 /// </summary> 91 /// <param name="Pdf_filePath">要签字的pdf文件路径</param> 92 /// <param name="SignImgPath">签字的图片路径</param> 93 /// <param name="SignKeyWord">关键字</param> 94 /// <returns></returns> 95 public static byte[] SignFile(string Pdf_filePath, string SignImgPath, string SignKeyWord) 96 { 97 byte[] newbytefile; 98 try 99 { 100 using (MemoryStream ms = new MemoryStream()) 101 { 102 // 创建一个PdfReader对象 103 using (PdfReader reader = new PdfReader(Pdf_filePath)) 104 { 105 using (PdfStamper stamper = new PdfStamper(reader, ms)) 106 { 107 // 获得文档页数 108 int n = reader.NumberOfPages; 109 for (int i = 1; i <= n; i++) 110 { 111 //获取当前页 112 PdfContentByte cb = stamper.GetOverContent(i); 113 PdfLocation pz = new PdfLocation(); 114 iTextSharp.text.pdf.parser.PdfReaderContentParser p = new iTextSharp.text.pdf.parser.PdfReaderContentParser(reader); 115 p.ProcessContent<PdfLocation>(i, pz); 116 //查找当前页的关键字 117 pz.SearchKeywords(SignKeyWord); 118 if (pz.TextLocationInfo.Count > 0) 119 { 120 XTextInfo o = pz.TextLocationInfo[0]; 121 //获取关键字左上角开始坐标 122 string[] L_T_Location = o.TopLeft.ToString().Split(',');//272.15,766.46,1 123 var left_x = float.Parse(L_T_Location[0]); 124 var top_y = float.Parse(L_T_Location[1]); 125 //获取关键字右下角结束坐标 126 string[] R_B_Location = o.BottomRight.ToString().Split(',');//305.15,755.46,1 127 var right_x = float.Parse(R_B_Location[0]); 128 var bottom_y = float.Parse(R_B_Location[1]); 129 //计算得到关键字的中心点 130 float x = (right_x - left_x) / 2 + left_x; 131 float y = (top_y - bottom_y) / 2 + bottom_y; 132 //创建一个图片对象 133 iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(SignImgPath); 134 float expectWidth = img.Width; 135 float expectHeight = img.Height; 136 if (img.Width > img.Height && img.Width > ReSizeMaxWidth) 137 { 138 expectWidth = ReSizeMaxWidth; 139 expectHeight = expectWidth * img.Height / img.Width; 140 } 141 else if (img.Height > img.Width && img.Height > ReSizeMaxHeight) 142 { 143 expectHeight = ReSizeMaxHeight; 144 expectWidth = expectHeight * img.Width / img.Height; 145 } 146 //设置图片的指定大小 147 img.ScaleToFit(expectWidth, expectHeight);//img.ScaleToFit(128, 128); 148 //设置图片位置在关键字正中心 149 img.SetAbsolutePosition(x - expectWidth / 2, y - expectHeight / 2);// 150 img.Transparency = new int[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 151 cb.AddImage(img); 152 } 153 } 154 } 155 } 156 newbytefile = ms.ToArray(); 157 } 158 return newbytefile; 159 } 160 catch (Exception ex) 161 { 162 South.Tools.Logger.SaveLogUtil.Error(ex.Message, ex, "SignPicPDF"); 163 return null; 164 } 165 } 166 167 public static System.Drawing.Image ConvertBase64ToImage(string base64String) 168 { 169 byte[] imageBytes = Convert.FromBase64String(base64String); 170 System.Drawing.Bitmap bitmap = null; 171 MemoryStream stream = null; 172 try 173 { 174 stream = new MemoryStream(imageBytes); 175 bitmap = new System.Drawing.Bitmap(stream); 176 //bitmap.Save(IOHelper.getPhysicalDir() + "/test.jpg", System.Drawing.Imaging.ImageFormat.Jpeg); 177 return bitmap; 178 } 179 catch (Exception) 180 { 181 182 throw; 183 } 184 finally 185 { 186 187 //if (stream != null) 188 //{ 189 // stream.Dispose(); 190 //} 191 } 192 193 } 194 }
2.查找关键字相关类
1 public class PdfLocation : LocationTextExtractionStrategy 2 { 3 private List<XTextChunk> m_locationResult = new List<XTextChunk>(); 4 private List<XTextInfo> m_TextLocationInfo = new List<XTextInfo>(); 5 public List<XTextChunk> LocationResult 6 { 7 get { return m_locationResult; } 8 } 9 public List<XTextInfo> TextLocationInfo 10 { 11 get { return m_TextLocationInfo; } 12 } 13 14 /// <summary> 15 /// Creates a new LocationTextExtracationStrategyEx 16 /// </summary> 17 public PdfLocation() 18 { 19 } 20 public void SearchKeywords(string sKeyword, bool bDefaultFirst = true) 21 { 22 m_locationResult.Sort(); 23 m_TextLocationInfo.Clear(); 24 //StringBuilder sb = new StringBuilder(); 25 XTextChunk lastChunk = null; 26 XTextInfo lastXTextInfo = null; 27 foreach (XTextChunk chunk in m_locationResult) 28 { 29 if (lastChunk == null) 30 { 31 //sb.Append(chunk.Text); 32 lastXTextInfo = new XTextInfo(chunk); 33 //if (lastXTextInfo.Text.Contains(sKeyword)) 34 //{ 35 // m_TextLocationInfo.Add(lastXTextInfo); 36 //} 37 } 38 else 39 { 40 if (chunk.sameLine(lastChunk)) 41 { 42 float dist = chunk.distanceFromEndOf(lastChunk); 43 44 if (dist < -chunk.CharSpaceWidth) 45 { 46 //sb.Append(' '); 47 lastXTextInfo.addSpace(); 48 } 49 //append a space if the trailing char of the prev string wasn't a space && the 1st char of the current string isn't a space 50 else if (dist > chunk.CharSpaceWidth / 2.0f && chunk.Text[0] != ' ' && lastChunk.Text[lastChunk.Text.Length - 1] != ' ') 51 { 52 //sb.Append(' '); 53 lastXTextInfo.addSpace(); 54 } 55 //sb.Append(chunk.Text); 56 lastXTextInfo.appendText(chunk); 57 } 58 else 59 { 60 //sb.Append('\n'); 61 //sb.Append(chunk.Text); 62 lastXTextInfo = new XTextInfo(chunk); 63 //if (lastXTextInfo.Text.Contains(sKeyword)) 64 //{ 65 // m_TextLocationInfo.Add(lastXTextInfo); 66 //} 67 } 68 } 69 lastChunk = chunk; 70 if (lastXTextInfo.Text.Contains(sKeyword)) 71 { 72 m_TextLocationInfo.Add(lastXTextInfo); 73 break; 74 } 75 } 76 } 77 /// <summary> 78 /// Returns the result so far 79 /// </summary> 80 /// <returns>a String with the resulting text</returns> 81 public override String GetResultantText() 82 { 83 m_locationResult.Sort(); 84 85 StringBuilder sb = new StringBuilder(); 86 XTextChunk lastChunk = null; 87 XTextInfo lastXTextInfo = null; 88 foreach (XTextChunk chunk in m_locationResult) 89 { 90 if (lastChunk == null) 91 { 92 sb.Append(chunk.Text); 93 lastXTextInfo = new XTextInfo(chunk); 94 m_TextLocationInfo.Add(lastXTextInfo); 95 } 96 else 97 { 98 if (chunk.sameLine(lastChunk)) 99 { 100 float dist = chunk.distanceFromEndOf(lastChunk); 101 102 if (dist < -chunk.CharSpaceWidth) 103 { 104 sb.Append(' '); 105 lastXTextInfo.addSpace(); 106 } 107 //append a space if the trailing char of the prev string wasn't a space && the 1st char of the current string isn't a space 108 else if (dist > chunk.CharSpaceWidth / 2.0f && chunk.Text[0] != ' ' && lastChunk.Text[lastChunk.Text.Length - 1] != ' ') 109 { 110 sb.Append(' '); 111 lastXTextInfo.addSpace(); 112 } 113 sb.Append(chunk.Text); 114 lastXTextInfo.appendText(chunk); 115 } 116 else 117 { 118 sb.Append('\n'); 119 sb.Append(chunk.Text); 120 lastXTextInfo = new XTextInfo(chunk); 121 m_TextLocationInfo.Add(lastXTextInfo); 122 } 123 } 124 lastChunk = chunk; 125 } 126 return sb.ToString(); 127 } 128 129 /// <summary> 130 /// 131 /// </summary> 132 /// <param name="renderInfo"></param> 133 public override void RenderText(TextRenderInfo renderInfo) 134 { 135 LineSegment segment = renderInfo.GetBaseline(); 136 XTextChunk location = new XTextChunk(renderInfo.GetText(), segment.GetStartPoint(), segment.GetEndPoint(), renderInfo.GetSingleSpaceWidth(), renderInfo.GetAscentLine(), renderInfo.GetDescentLine()); 137 m_locationResult.Add(location); 138 } 139 140 141 } 142 public class XTextChunk : IComparable, ICloneable 143 { 144 string m_text; 145 Vector m_startLocation; 146 Vector m_endLocation; 147 Vector m_orientationVector; 148 int m_orientationMagnitude; 149 int m_distPerpendicular; 150 float m_distParallelStart; 151 float m_distParallelEnd; 152 float m_charSpaceWidth; 153 154 public LineSegment AscentLine; 155 public LineSegment DecentLine; 156 157 public object Clone() 158 { 159 XTextChunk copy = new XTextChunk(m_text, m_startLocation, m_endLocation, m_charSpaceWidth, AscentLine, DecentLine); 160 return copy; 161 } 162 163 public string Text 164 { 165 get { return m_text; } 166 set { m_text = value; } 167 } 168 public float CharSpaceWidth 169 { 170 get { return m_charSpaceWidth; } 171 set { m_charSpaceWidth = value; } 172 } 173 public Vector StartLocation 174 { 175 get { return m_startLocation; } 176 set { m_startLocation = value; } 177 } 178 public Vector EndLocation 179 { 180 get { return m_endLocation; } 181 set { m_endLocation = value; } 182 } 183 184 /// <summary> 185 /// Represents a chunk of text, it's orientation, and location relative to the orientation vector 186 /// </summary> 187 /// <param name="txt"></param> 188 /// <param name="startLoc"></param> 189 /// <param name="endLoc"></param> 190 /// <param name="charSpaceWidth"></param> 191 public XTextChunk(string txt, Vector startLoc, Vector endLoc, float charSpaceWidth, LineSegment ascentLine, LineSegment decentLine) 192 { 193 m_text = txt; 194 m_startLocation = startLoc; 195 m_endLocation = endLoc; 196 m_charSpaceWidth = charSpaceWidth; 197 AscentLine = ascentLine; 198 DecentLine = decentLine; 199 200 m_orientationVector = m_endLocation.Subtract(m_startLocation).Normalize(); 201 m_orientationMagnitude = (int)(Math.Atan2(m_orientationVector[Vector.I2], m_orientationVector[Vector.I1]) * 1000); 202 203 // the two vectors we are crossing are in the same plane, so the result will be purely 204 // in the z-axis (out of plane) direction, so we just take the I3 component of the result 205 Vector origin = new Vector(0, 0, 1); 206 m_distPerpendicular = (int)(m_startLocation.Subtract(origin)).Cross(m_orientationVector)[Vector.I3]; 207 208 m_distParallelStart = m_orientationVector.Dot(m_startLocation); 209 m_distParallelEnd = m_orientationVector.Dot(m_endLocation); 210 } 211 212 /// <summary> 213 /// true if this location is on the the same line as the other text chunk 214 /// </summary> 215 /// <param name="XTextChunkToCompare">the location to compare to</param> 216 /// <returns>true if this location is on the the same line as the other</returns> 217 public bool sameLine(XTextChunk XTextChunkToCompare) 218 { 219 if (m_orientationMagnitude != XTextChunkToCompare.m_orientationMagnitude) return false; 220 if (m_distPerpendicular != XTextChunkToCompare.m_distPerpendicular) return false; 221 return true; 222 } 223 224 /// <summary> 225 /// Computes the distance between the end of 'other' and the beginning of this chunk 226 /// in the direction of this chunk's orientation vector. Note that it's a bad idea 227 /// to call this for chunks that aren't on the same line and orientation, but we don't 228 /// explicitly check for that condition for performance reasons. 229 /// </summary> 230 /// <param name="other"></param> 231 /// <returns>the number of spaces between the end of 'other' and the beginning of this chunk</returns> 232 public float distanceFromEndOf(XTextChunk other) 233 { 234 float distance = m_distParallelStart - other.m_distParallelEnd; 235 return distance; 236 } 237 238 /// <summary> 239 /// Compares based on orientation, perpendicular distance, then parallel distance 240 /// </summary> 241 /// <param name="obj"></param> 242 /// <returns></returns> 243 public int CompareTo(object obj) 244 { 245 if (obj == null) throw new ArgumentException("Object is now a XTextChunk"); 246 247 XTextChunk rhs = obj as XTextChunk; 248 if (rhs != null) 249 { 250 if (this == rhs) return 0; 251 252 int rslt; 253 rslt = m_orientationMagnitude - rhs.m_orientationMagnitude; 254 if (rslt != 0) return rslt; 255 256 rslt = m_distPerpendicular - rhs.m_distPerpendicular; 257 if (rslt != 0) return rslt; 258 259 // note: it's never safe to check floating point numbers for equality, and if two chunks 260 // are truly right on top of each other, which one comes first or second just doesn't matter 261 // so we arbitrarily choose this way. 262 rslt = m_distParallelStart < rhs.m_distParallelStart ? -1 : 1; 263 264 return rslt; 265 } 266 else 267 { 268 throw new ArgumentException("Object is now a XTextChunk"); 269 } 270 } 271 } 272 273 public class XTextInfo 274 { 275 public Vector TopLeft; 276 public Vector BottomRight; 277 private string m_Text; 278 279 public string Text 280 { 281 get { return m_Text; } 282 } 283 284 /// <summary> 285 /// Create a XTextInfo. 286 /// </summary> 287 /// <param name="initialXTextChunk"></param> 288 public XTextInfo(XTextChunk initialXTextChunk) 289 { 290 TopLeft = initialXTextChunk.AscentLine.GetStartPoint(); 291 BottomRight = initialXTextChunk.DecentLine.GetEndPoint(); 292 //TopLeft = initialXTextChunk.StartLocation; 293 //BottomRight = initialXTextChunk.EndLocation; 294 m_Text = initialXTextChunk.Text; 295 } 296 297 /// <summary> 298 /// Add more text to this XTextInfo. 299 /// </summary> 300 /// <param name="additionalXTextChunk"></param> 301 public void appendText(XTextChunk additionalXTextChunk) 302 { 303 BottomRight = additionalXTextChunk.DecentLine.GetEndPoint(); 304 //BottomRight = additionalXTextChunk.EndLocation; 305 m_Text += additionalXTextChunk.Text; 306 } 307 308 /// <summary> 309 /// Add a space to the XTextInfo. This will leave the endpoint out of sync with the text. 310 /// The assumtion is that you will add more text after the space which will correct the endpoint. 311 /// </summary> 312 public void addSpace() 313 { 314 m_Text += ' '; 315 } 316 }
加载全部内容