.net通过iTextSharp.pdf操作pdf文件实现查找关键字签字盖章

之前这个事情都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     }

View Code

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     }

View Code

 

Tags: