直接说问题,itext没有直接提供替换PDF中文本的接口(查看资料得到的结论是PDF不支持这种操作),不过存在解决思路:在需要替换的文本上覆盖新的文本。按照这个思路我们需要解决以下几个问题:

  • itext怎样增加白色底的覆盖层
  • 找到覆盖层的位置(左顶点的位置)和高度与宽带
这样做的目的是什么了?也告诉下大家,比如:现在要你将业务数据导出成PDF存档,且PDF的模板有现成的。对我们写程序的来说,变化的只是部分数据,假如我们可以直接替换里面的数据,是不是可以节省我们的开发时间。

1、itext怎样增加覆盖层?

itext在自己的Demo中提供了很多案例代码,从中我们可以看到高亮的案例
查看itext代码

  1. /*
  2. * This example was written in answer to the question:
  3. * http://stackoverflow.com/questions/33952183
  4. */
  5. package sandbox.stamper;
  6. import com.itextpdf.text.BaseColor;
  7. import com.itextpdf.text.DocumentException;
  8. import com.itextpdf.text.pdf.PdfContentByte;
  9. import com.itextpdf.text.pdf.PdfReader;
  10. import com.itextpdf.text.pdf.PdfStamper;
  11. import java.io.File;
  12. import java.io.FileOutputStream;
  13. import java.io.IOException;
  14. /**
  15. *
  16. * @author Bruno Lowagie (iText Software)
  17. */
  18. public class HighLightByAddingContent {
  19. public static final String SRC = "resources/pdfs/hello.pdf";
  20. public static final String DEST = "results/stamper/hello_highlighted.pdf";
  21. public static void main(String[] args) throws IOException, DocumentException {
  22. File file = new File(DEST);
  23. file.getParentFile().mkdirs();
  24. new HighLightByAddingContent().manipulatePdf(SRC, DEST);
  25. }
  26. public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
  27. PdfReader reader = new PdfReader(src);
  28. PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
  29. PdfContentByte canvas = stamper.getUnderContent(1);
  30. canvas.saveState();
  31. canvas.setColorFill(BaseColor.YELLOW);
  32. canvas.rectangle(36, 786, 66, 16);
  33. canvas.fill();
  34. canvas.restoreState();
  35. stamper.close();
  36. reader.close();
  37. }
  38. }

这里可以在任意位置产生一个层,符合我们的“遮盖层”的要求,不过,通过测试发现此段代码存在一个问题点,它无法遮挡住文字,只是添加了一个背景层。为了达到我们的要求,我们只需要修改一处地方:

  1. PdfContentByte canvas = stamper.getUnderContent(1);  //变成 PdfContentByte canvas = stamper.getOverContent(1);

到目前为止,我们的遮盖层已添加,后面我们还需要的就是在新的遮盖层上写上自己的文字,代码如下:

  1. /**********************************************************************
  2. * <pre>
  3. * FILE : HighLightByAddingContent.java
  4. * CLASS : HighLightByAddingContent
  5. *
  6. *
  7. * FUNCTION : TODO
  8. *
  9. *
  10. *======================================================================
  11. * CHANGE HISTORY LOG
  12. *----------------------------------------------------------------------
  13. * MOD. NO.|   DATE   |   NAME  | REASON  | CHANGE REQ.
  14. *----------------------------------------------------------------------
  15. *
  16. * DESCRIPTION:
  17. * </pre>
  18. ***********************************************************************/
  19. package com.cx.itext;
  20. import java.io.File;
  21. import java.io.FileOutputStream;
  22. import java.io.IOException;
  23. import java.net.URLDecoder;
  24. import com.itextpdf.text.BaseColor;
  25. import com.itextpdf.text.DocumentException;
  26. import com.itextpdf.text.Font;
  27. import com.itextpdf.text.pdf.BaseFont;
  28. import com.itextpdf.text.pdf.PdfContentByte;
  29. import com.itextpdf.text.pdf.PdfReader;
  30. import com.itextpdf.text.pdf.PdfStamper;
  31. public class HighLightByAddingContent {
  32. @SuppressWarnings("deprecation")
  33. public static final String SRC = URLDecoder.decode(HighLightByAddingContent.class.getResource("ticket.pdf").getFile());
  34. public static final String DEST = "I://ticket.pdf";
  35. public static void main(String[] args) throws IOException, DocumentException {
  36. File file = new File(DEST);
  37. file.getParentFile().mkdirs();
  38. new HighLightByAddingContent().manipulatePdf(SRC, DEST);
  39. }
  40. public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
  41. PdfReader reader = new PdfReader(src);
  42. PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
  43. PdfContentByte canvas = stamper.getOverContent(1);
  44. float height=595;
  45. System.out.println(canvas.getHorizontalScaling());
  46. float x,y;
  47. x= 216;
  48. y = height -49.09F;
  49. canvas.saveState();
  50. canvas.setColorFill(BaseColor.WHITE);
  51. canvas.rectangle(x, y-5, 43, 15);
  52. canvas.fill();
  53. canvas.restoreState();
  54. //开始写入文本
  55. canvas.beginText();
  56. //BaseFont bf = BaseFont.createFont(URLDecoder.decode(CutAndPaste.class.getResource("/AdobeSongStd-Light.otf").getFile()), BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
  57. BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
  58. Font font = new Font(bf,10,Font.BOLD);
  59. //设置字体和大小
  60. canvas.setFontAndSize(font.getBaseFont(), 10);
  61. //设置字体的输出位置
  62. canvas.setTextMatrix(x, y);
  63. //要输出的text
  64. canvas.showText("多退少补" );
  65. //设置字体的输出位置
  66. canvas.setFontAndSize(font.getBaseFont(), 20);
  67. canvas.setTextMatrix(x, y-90);
  68. //要输出的text
  69. canvas.showText("多退少补" );
  70. canvas.endText();
  71. stamper.close();
  72. reader.close();
  73. System.out.println("complete");
  74. }
  75. }

2、找到覆盖层的位置(左顶点的位置)和高度与宽带

我的第一个想法是通过工具得到替换文本的具体位置,虽然这个方法不怎么好,不过确实可行。使用到的工具是常用的Adobe Reader,以下是正常页面(PDF是网上搜的,百度key:“申请 filetype:pdf”):
 
Adobe提供了测量工具,我们可以通过“编辑-->分析-->测量工具”看到如下页面:
此时,我们虽然可以直接测量,但是测量默认显示的厘米,与itext需要设置的单位不一致,我们需要手工换算下(1英寸=72点)。不过,adobe可以帮我们省掉换算的工作,右键点击,出现以下选项(需要在测量功能下右键):
“更改比例”可以帮助我们完成换算工作。(ps:“显示标尺”是一个不错的选项)。最后的画面如下:
最后,需要提醒下,itext的Y是从下往上算的。
 
这样得到位置是不是太不方便了。那我们是否可以通过itext自动计算出我们需要的位置?代码如下(从网上COPY,不记得具体来源,支持作者)
  1. /**********************************************************************
  2. * <pre>
  3. * FILE : Demo.java
  4. * CLASS : Demo
  5. *
  6. * AUTHOR : caoxu-yiyang@qq.com
  7. *
  8. * FUNCTION : TODO
  9. *
  10. *
  11. *======================================================================
  12. * CHANGE HISTORY LOG
  13. *----------------------------------------------------------------------
  14. * MOD. NO.|   DATE   |   NAME  | REASON  | CHANGE REQ.
  15. *----------------------------------------------------------------------
  16. *          |2016年11月9日|caoxu-yiyang@qq.com| Created |
  17. * DESCRIPTION:
  18. * </pre>
  19. ***********************************************************************/
  20. package com.cx.itext;
  21. import java.io.IOException;
  22. import com.itextpdf.awt.geom.Rectangle2D.Float;
  23. import com.itextpdf.text.pdf.PdfReader;
  24. import com.itextpdf.text.pdf.parser.ImageRenderInfo;
  25. import com.itextpdf.text.pdf.parser.PdfReaderContentParser;
  26. import com.itextpdf.text.pdf.parser.RenderListener;
  27. import com.itextpdf.text.pdf.parser.TextRenderInfo;
  28. public class Demo
  29. {
  30. // 定义关键字
  31. private static String KEY_WORD = "结算区分";
  32. // 定义返回值
  33. private static float[] resu = null;
  34. // 定义返回页码
  35. private static int i = 0;
  36. public static void main(String[] args) {
  37. float[] point = getKeyWords("I://ticket_in.pdf");
  38. }
  39. /*
  40. * 返回关键字所在的坐标和页数 float[0] >> X float[1] >> Y float[2] >> page
  41. */
  42. private static float[] getKeyWords(String filePath)
  43. {
  44. try
  45. {
  46. PdfReader pdfReader = new PdfReader(filePath);
  47. int pageNum = pdfReader.getNumberOfPages();
  48. PdfReaderContentParser pdfReaderContentParser = new PdfReaderContentParser(
  49. pdfReader);
  50. // 下标从1开始
  51. for (i = 1; i <= pageNum; i++)
  52. {
  53. pdfReaderContentParser.processContent(i, new RenderListener()
  54. {
  55. @Override
  56. public void renderText(TextRenderInfo textRenderInfo)
  57. {
  58. String text = textRenderInfo.getText();
  59. if (null != text && text.contains(KEY_WORD))
  60. {
  61. Float boundingRectange = textRenderInfo
  62. .getBaseline().getBoundingRectange();
  63. resu = new float[3];
  64. System.out.println("======="+text);
  65. System.out.println("h:"+boundingRectange.getHeight());
  66. System.out.println("w:"+boundingRectange.width);
  67. System.out.println("centerX:"+boundingRectange.getCenterX());
  68. System.out.println("centerY:"+boundingRectange.getCenterY());
  69. System.out.println("x:"+boundingRectange.getX());
  70. System.out.println("y:"+boundingRectange.getY());
  71. System.out.println("maxX:"+boundingRectange.getMaxX());
  72. System.out.println("maxY:"+boundingRectange.getMaxY());
  73. System.out.println("minX:"+boundingRectange.getMinX());
  74. System.out.println("minY:"+boundingRectange.getMinY());
  75. resu[0] = boundingRectange.x;
  76. resu[1] = boundingRectange.y;
  77. resu[2] = i;
  78. }
  79. }
  80. @Override
  81. public void renderImage(ImageRenderInfo arg0)
  82. {
  83. }
  84. @Override
  85. public void endTextBlock()
  86. {
  87. }
  88. @Override
  89. public void beginTextBlock()
  90. {
  91. }
  92. });
  93. }
  94. } catch (IOException e)
  95. {
  96. e.printStackTrace();
  97. }
  98. return resu;
  99. }
  100. }

结合以上的,我们就可以写一个自动替换PDF文本的类,具体使用如下:

  1. public static void main(String[] args) throws IOException, DocumentException {
  2. PdfReplacer textReplacer = new PdfReplacer("I://test.pdf");
  3. textReplacer.replaceText("陈坤", "小白");
  4. textReplacer.replaceText("本科", "社会大学");
  5. textReplacer.replaceText("0755-29493863", "15112345678");
  6. textReplacer.toPdf("I://ticket_out.pdf");
  7. }

原始PDF:

替换之后的(红色背景只是方便大家看到差别):
(第一次认真写博客,感觉感觉好花时间了,佩服那些坚持写博客的人~~)

补上相关代码(还在完善中),总共4个类

代码中有几个地方要说明下:

1、由于自动计算得到的高度都是0,所有我这边默认的都是12,大家要根据实际情况来设

2、除了可以让代码自己计算位置之外,也可以通过replaceText的重载方法强制指定替换区域。

  1. /**********************************************************************
  2. * <pre>
  3. * FILE : PdfTextReplacer.java
  4. * CLASS : PdfTextReplacer
  5. *
  6. * AUTHOR : caoxu-yiyang@qq.com
  7. *
  8. * FUNCTION : TODO
  9. *
  10. *
  11. *======================================================================
  12. * CHANGE HISTORY LOG
  13. *----------------------------------------------------------------------
  14. * MOD. NO.|   DATE   |   NAME  | REASON  | CHANGE REQ.
  15. *----------------------------------------------------------------------
  16. *          |2016年11月8日|caoxu-yiyang@qq.com| Created |
  17. * DESCRIPTION:
  18. * </pre>
  19. ***********************************************************************/
  20. package com.cx.itext;
  21. import java.io.ByteArrayOutputStream;
  22. import java.io.FileInputStream;
  23. import java.io.FileOutputStream;
  24. import java.io.IOException;
  25. import java.util.HashMap;
  26. import java.util.Map;
  27. import java.util.Map.Entry;
  28. import java.util.Set;
  29. import com.itextpdf.text.BaseColor;
  30. import com.itextpdf.text.DocumentException;
  31. import com.itextpdf.text.Font;
  32. import com.itextpdf.text.log.Logger;
  33. import com.itextpdf.text.log.LoggerFactory;
  34. import com.itextpdf.text.pdf.BaseFont;
  35. import com.itextpdf.text.pdf.PdfContentByte;
  36. import com.itextpdf.text.pdf.PdfReader;
  37. import com.itextpdf.text.pdf.PdfStamper;
  38. /**
  39. * 替换PDF文件某个区域内的文本
  40. * @user : caoxu-yiyang@qq.com
  41. * @date : 2016年11月8日
  42. */
  43. public class PdfReplacer {
  44. private static final Logger logger = LoggerFactory.getLogger(PdfReplacer.class);
  45. private int fontSize;
  46. private Map<String, ReplaceRegion> replaceRegionMap = new HashMap<String, ReplaceRegion>();
  47. private Map<String, Object> replaceTextMap =new HashMap<String, Object>();
  48. private ByteArrayOutputStream output;
  49. private PdfReader reader;
  50. private PdfStamper stamper;
  51. private PdfContentByte canvas;
  52. private Font font;
  53. public PdfReplacer(byte[] pdfBytes) throws DocumentException, IOException{
  54. init(pdfBytes);
  55. }
  56. public PdfReplacer(String fileName) throws IOException, DocumentException{
  57. FileInputStream in = null;
  58. try{
  59. in =new FileInputStream(fileName);
  60. byte[] pdfBytes = new byte[in.available()];
  61. in.read(pdfBytes);
  62. init(pdfBytes);
  63. }finally{
  64. in.close();
  65. }
  66. }
  67. private void init(byte[] pdfBytes) throws DocumentException, IOException{
  68. logger.info("初始化开始");
  69. reader = new PdfReader(pdfBytes);
  70. output = new ByteArrayOutputStream();
  71. stamper = new PdfStamper(reader, output);
  72. canvas = stamper.getOverContent(1);
  73. setFont(10);
  74. logger.info("初始化成功");
  75. }
  76. private void close() throws DocumentException, IOException{
  77. if(reader != null){
  78. reader.close();
  79. }
  80. if(output != null){
  81. output.close();
  82. }
  83. output=null;
  84. replaceRegionMap=null;
  85. replaceTextMap=null;
  86. }
  87. public void replaceText(float x, float y, float w,float h, String text){
  88. ReplaceRegion region = new ReplaceRegion(text);     //用文本作为别名
  89. region.setH(h);
  90. region.setW(w);
  91. region.setX(x);
  92. region.setY(y);
  93. addReplaceRegion(region);
  94. this.replaceText(text, text);
  95. }
  96. public void replaceText(String name, String text){
  97. this.replaceTextMap.put(name, text);
  98. }
  99. /**
  100. * 替换文本
  101. * @throws IOException
  102. * @throws DocumentException
  103. * @user : caoxu-yiyang@qq.com
  104. * @date : 2016年11月9日
  105. */
  106. private void process() throws DocumentException, IOException{
  107. try{
  108. parseReplaceText();
  109. canvas.saveState();
  110. Set<Entry<String, ReplaceRegion>> entrys = replaceRegionMap.entrySet();
  111. for (Entry<String, ReplaceRegion> entry : entrys) {
  112. ReplaceRegion value = entry.getValue();
  113. canvas.setColorFill(BaseColor.RED);
  114. canvas.rectangle(value.getX(),value.getY(),value.getW(),value.getH());
  115. }
  116. canvas.fill();
  117. canvas.restoreState();
  118. //开始写入文本
  119. canvas.beginText();
  120. for (Entry<String, ReplaceRegion> entry : entrys) {
  121. ReplaceRegion value = entry.getValue();
  122. //设置字体
  123. canvas.setFontAndSize(font.getBaseFont(), getFontSize());
  124. canvas.setTextMatrix(value.getX(),value.getY()+2/*修正背景与文本的相对位置*/);
  125. canvas.showText((String) replaceTextMap.get(value.getAliasName()));
  126. }
  127. canvas.endText();
  128. }finally{
  129. if(stamper != null){
  130. stamper.close();
  131. }
  132. }
  133. }
  134. /**
  135. * 未指定具体的替换位置时,系统自动查找位置
  136. * @user : caoxu-yiyang@qq.com
  137. * @date : 2016年11月9日
  138. */
  139. private void parseReplaceText() {
  140. PdfPositionParse parse = new PdfPositionParse(reader);
  141. Set<Entry<String, Object>> entrys = this.replaceTextMap.entrySet();
  142. for (Entry<String, Object> entry : entrys) {
  143. if(this.replaceRegionMap.get(entry.getKey()) == null){
  144. parse.addFindText(entry.getKey());
  145. }
  146. }
  147. try {
  148. Map<String, ReplaceRegion> parseResult = parse.parse();
  149. Set<Entry<String, ReplaceRegion>> parseEntrys = parseResult.entrySet();
  150. for (Entry<String, ReplaceRegion> entry : parseEntrys) {
  151. if(entry.getValue() != null){
  152. this.replaceRegionMap.put(entry.getKey(), entry.getValue());
  153. }
  154. }
  155. } catch (IOException e) {
  156. logger.error(e.getMessage(), e);
  157. }
  158. }
  159. /**
  160. * 生成新的PDF文件
  161. * @user : caoxu-yiyang@qq.com
  162. * @date : 2016年11月9日
  163. * @param fileName
  164. * @throws DocumentException
  165. * @throws IOException
  166. */
  167. public void toPdf(String fileName) throws DocumentException, IOException{
  168. FileOutputStream fileOutputStream = null;
  169. try{
  170. process();
  171. fileOutputStream = new FileOutputStream(fileName);
  172. fileOutputStream.write(output.toByteArray());
  173. fileOutputStream.flush();
  174. }catch(IOException e){
  175. logger.error(e.getMessage(), e);
  176. throw e;
  177. }finally{
  178. if(fileOutputStream != null){
  179. fileOutputStream.close();
  180. }
  181. close();
  182. }
  183. logger.info("文件生成成功");
  184. }
  185. /**
  186. * 将生成的PDF文件转换成二进制数组
  187. * @user : caoxu-yiyang@qq.com
  188. * @date : 2016年11月9日
  189. * @return
  190. * @throws DocumentException
  191. * @throws IOException
  192. */
  193. public byte[] toBytes() throws DocumentException, IOException{
  194. try{
  195. process();
  196. logger.info("二进制数据生成成功");
  197. return output.toByteArray();
  198. }finally{
  199. close();
  200. }
  201. }
  202. /**
  203. * 添加替换区域
  204. * @user : caoxu-yiyang@qq.com
  205. * @date : 2016年11月9日
  206. * @param replaceRegion
  207. */
  208. public void addReplaceRegion(ReplaceRegion replaceRegion){
  209. this.replaceRegionMap.put(replaceRegion.getAliasName(), replaceRegion);
  210. }
  211. /**
  212. * 通过别名得到替换区域
  213. * @user : caoxu-yiyang@qq.com
  214. * @date : 2016年11月9日
  215. * @param aliasName
  216. * @return
  217. */
  218. public ReplaceRegion getReplaceRegion(String aliasName){
  219. return this.replaceRegionMap.get(aliasName);
  220. }
  221. public int getFontSize() {
  222. return fontSize;
  223. }
  224. /**
  225. * 设置字体大小
  226. * @user : caoxu-yiyang@qq.com
  227. * @date : 2016年11月9日
  228. * @param fontSize
  229. * @throws DocumentException
  230. * @throws IOException
  231. */
  232. public void setFont(int fontSize) throws DocumentException, IOException{
  233. if(fontSize != this.fontSize){
  234. this.fontSize = fontSize;
  235. BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
  236. font = new Font(bf,this.fontSize,Font.BOLD);
  237. }
  238. }
  239. public void setFont(Font font){
  240. if(font == null){
  241. throw new NullPointerException("font is null");
  242. }
  243. this.font = font;
  244. }
  245. public static void main(String[] args) throws IOException, DocumentException {
  246. PdfReplacer textReplacer = new PdfReplacer("I://test.pdf");
  247. textReplacer.replaceText("陈坤", "小白");
  248. textReplacer.replaceText("本科", "社会大学");
  249. textReplacer.replaceText("0755-29493863", "15112345678");
  250. textReplacer.toPdf("I://ticket_out.pdf");
  251. }
  252. }
  1. /**********************************************************************
  2. * <pre>
  3. * FILE : ReplaceRegion.java
  4. * CLASS : ReplaceRegion
  5. *
  6. * AUTHOR : caoxu-yiyang@qq.com
  7. *
  8. * FUNCTION : TODO
  9. *
  10. *
  11. *======================================================================
  12. * CHANGE HISTORY LOG
  13. *----------------------------------------------------------------------
  14. * MOD. NO.|   DATE   |   NAME  | REASON  | CHANGE REQ.
  15. *----------------------------------------------------------------------
  16. *          |2016年11月9日|caoxu-yiyang@qq.com| Created |
  17. * DESCRIPTION:
  18. * </pre>
  19. ***********************************************************************/
  20. package com.cx.itext;
  21. /**
  22. * 需要替换的区域
  23. * @user : caoxu-yiyang@qq.com
  24. * @date : 2016年11月9日
  25. */
  26. public class ReplaceRegion {
  27. private String aliasName;
  28. private Float x;
  29. private Float y;
  30. private Float w;
  31. private Float h;
  32. public ReplaceRegion(String aliasName){
  33. this.aliasName = aliasName;
  34. }
  35. /**
  36. * 替换区域的别名
  37. * @user : caoxu-yiyang@qq.com
  38. * @date : 2016年11月9日
  39. * @return
  40. */
  41. public String getAliasName() {
  42. return aliasName;
  43. }
  44. public void setAliasName(String aliasName) {
  45. this.aliasName = aliasName;
  46. }
  47. public Float getX() {
  48. return x;
  49. }
  50. public void setX(Float x) {
  51. this.x = x;
  52. }
  53. public Float getY() {
  54. return y;
  55. }
  56. public void setY(Float y) {
  57. this.y = y;
  58. }
  59. public Float getW() {
  60. return w;
  61. }
  62. public void setW(Float w) {
  63. this.w = w;
  64. }
  65. public Float getH() {
  66. return h;
  67. }
  68. public void setH(Float h) {
  69. this.h = h;
  70. }
  71. }
  1. /**********************************************************************
  2. * <pre>
  3. * FILE : PdfPositionParse.java
  4. * CLASS : PdfPositionParse
  5. *
  6. * AUTHOR : caoxu-yiyang@qq.com
  7. *
  8. * FUNCTION : TODO
  9. *
  10. *
  11. *======================================================================
  12. * CHANGE HISTORY LOG
  13. *----------------------------------------------------------------------
  14. * MOD. NO.|   DATE   |   NAME  | REASON  | CHANGE REQ.
  15. *----------------------------------------------------------------------
  16. *          |2016年11月9日|caoxu-yiyang@qq.com| Created |
  17. * DESCRIPTION:
  18. * </pre>
  19. ***********************************************************************/
  20. package com.cx.itext;
  21. import java.io.FileInputStream;
  22. import java.io.IOException;
  23. import java.util.ArrayList;
  24. import java.util.List;
  25. import java.util.Map;
  26. import com.cx.itext.listener.PositionRenderListener;
  27. import com.itextpdf.text.pdf.PdfReader;
  28. import com.itextpdf.text.pdf.parser.PdfReaderContentParser;
  29. /**
  30. * 解析PDF中文本的x,y位置
  31. * @user : caoxu-yiyang@qq.com
  32. * @date : 2016年11月9日
  33. */
  34. public class PdfPositionParse {
  35. private PdfReader reader;
  36. private List<String> findText = new ArrayList<String>();    //需要查找的文本
  37. private PdfReaderContentParser parser;
  38. public PdfPositionParse(String fileName) throws IOException{
  39. FileInputStream in = null;
  40. try{
  41. in =new FileInputStream(fileName);
  42. byte[] bytes = new byte[in.available()];
  43. in.read(bytes);
  44. init(bytes);
  45. }finally{
  46. in.close();
  47. }
  48. }
  49. public PdfPositionParse(byte[] bytes) throws IOException{
  50. init(bytes);
  51. }
  52. private boolean needClose = true;
  53. /**
  54. * 传递进来的reader不会在PdfPositionParse结束时关闭
  55. * @user : caoxu-yiyang@qq.com
  56. * @date : 2016年11月9日
  57. * @param reader
  58. */
  59. public PdfPositionParse(PdfReader reader){
  60. this.reader = reader;
  61. parser = new PdfReaderContentParser(reader);
  62. needClose = false;
  63. }
  64. public void addFindText(String text){
  65. this.findText.add(text);
  66. }
  67. private void init(byte[] bytes) throws IOException {
  68. reader = new PdfReader(bytes);
  69. parser = new PdfReaderContentParser(reader);
  70. }
  71. /**
  72. * 解析文本
  73. * @user : caoxu-yiyang@qq.com
  74. * @date : 2016年11月9日
  75. * @throws IOException
  76. */
  77. public Map<String, ReplaceRegion> parse() throws IOException{
  78. try{
  79. if(this.findText.size() == 0){
  80. throw new NullPointerException("没有需要查找的文本");
  81. }
  82. PositionRenderListener listener = new PositionRenderListener(this.findText);
  83. parser.processContent(1, listener);
  84. return listener.getResult();
  85. }finally{
  86. if(reader != null && needClose){
  87. reader.close();
  88. }
  89. }
  90. }
  91. }
  1. /**********************************************************************
  2. * <pre>
  3. * FILE : PositionRenderListener.java
  4. * CLASS : PositionRenderListener
  5. *
  6. * AUTHOR : caoxu-yiyang@qq.com
  7. *
  8. * FUNCTION : TODO
  9. *
  10. *
  11. *======================================================================
  12. * CHANGE HISTORY LOG
  13. *----------------------------------------------------------------------
  14. * MOD. NO.|   DATE   |   NAME  | REASON  | CHANGE REQ.
  15. *----------------------------------------------------------------------
  16. *          |2016年11月9日|caoxu-yiyang@qq.com| Created |
  17. * DESCRIPTION:
  18. * </pre>
  19. ***********************************************************************/
  20. package com.cx.itext.listener;
  21. import java.util.HashMap;
  22. import java.util.List;
  23. import java.util.Map;
  24. import com.cx.itext.ReplaceRegion;
  25. import com.itextpdf.awt.geom.Rectangle2D.Float;
  26. import com.itextpdf.text.pdf.parser.ImageRenderInfo;
  27. import com.itextpdf.text.pdf.parser.RenderListener;
  28. import com.itextpdf.text.pdf.parser.TextRenderInfo;
  29. /**
  30. * pdf渲染监听,当找到渲染的文本时,得到文本的坐标x,y,w,h
  31. * @user : caoxu-yiyang@qq.com
  32. * @date : 2016年11月9日
  33. */
  34. public class PositionRenderListener implements RenderListener{
  35. private List<String> findText;
  36. private float defaultH;     ///出现无法取到值的情况,默认为12
  37. private float fixHeight;    //可能出现无法完全覆盖的情况,提供修正的参数,默认为2
  38. public PositionRenderListener(List<String> findText, float defaultH,float fixHeight) {
  39. this.findText = findText;
  40. this.defaultH = defaultH;
  41. this.fixHeight = fixHeight;
  42. }
  43. public PositionRenderListener(List<String> findText) {
  44. this.findText = findText;
  45. this.defaultH = 12;
  46. this.fixHeight = 2;
  47. }
  48. @Override
  49. public void beginTextBlock() {
  50. }
  51. @Override
  52. public void endTextBlock() {
  53. }
  54. @Override
  55. public void renderImage(ImageRenderInfo imageInfo) {
  56. }
  57. private Map<String, ReplaceRegion> result = new HashMap<String, ReplaceRegion>();
  58. @Override
  59. public void renderText(TextRenderInfo textInfo) {
  60. String text = textInfo.getText();
  61. for (String keyWord : findText) {
  62. if (null != text && text.equals(keyWord)){
  63. Float bound = textInfo.getBaseline().getBoundingRectange();
  64. ReplaceRegion region = new ReplaceRegion(keyWord);
  65. region.setH(bound.height == 0 ? defaultH : bound.height);
  66. region.setW(bound.width);
  67. region.setX(bound.x);
  68. region.setY(bound.y-this.fixHeight);
  69. result.put(keyWord, region);
  70. }
  71. }
  72. }
  73. public Map<String, ReplaceRegion> getResult() {
  74. for (String key : findText) {   //补充没有找到的数据
  75. if(this.result.get(key) == null){
  76. this.result.put(key, null);
  77. }
  78. }
  79. return this.result;
  80. }
  81. }

我用到的jar包如下:


大家可以从官网下载,可以构建maven项目省去自己找包的麻烦。如果没有用maven又想下载具体的jar包,可以直接访问maven仓库下载:http://mvnrepository.com/

使用itext直接替换PDF中的文本的更多相关文章

  1. java itext替换PDF中的文本

    itext没有提供直接替换PDF文本的接口,我们可以通过在原有的文本区域覆盖一个遮挡层,再在上面加上文本来实现. 所需jar包: 1.先在PDF需要替换的位置覆盖一个白色遮挡层(颜色可根据PDF文字背 ...

  2. servlet操作本地文件汇总: 判断文件是否存在;文件重命名;文件复制; 获取文件属性信息,转成Json对象; 获取指定类型的文件; 查找替换.txt中的文本

    package servlet; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; ...

  3. C# 设置或验证 PDF中的文本域格式

    概述 PDF中的文本域可以通过设置不同格式,用于显示数字.货币.日期.时间.邮政编码.电话号码和社保号等等.Adobe Acrobat提供了许多固定的JavaScripts用来设置和验证文本域的格式, ...

  4. Java 替换PDF中的字体

    文档中可通过应用不同的字体来呈现不一样的视觉效果,通过字体来实现文档布局.排版等设计需要.应用字体时,可在创建文档时指定字体,也可以用新字体去替换文档中已有的字体.下面,以Java代码展示如何来替换P ...

  5. Java 读取PDF中的文本和图片

    本文将介绍通过Java程序来读取PDF文档中的文本和图片的方法.分别调用方法extractText()和extractImages()来读取.   使用工具:Free Spire.PDF for Ja ...

  6. Java 设置PDF中的文本旋转、倾斜

    本文介绍通过Java程序在PDF文档中设置文本旋转.倾斜的方法.设置文本倾斜时,通过定义方法TransformText(page);并设置page.getCanvas().skewTransform( ...

  7. python 之文本搜索与替换文件中的文本

    #!/usr/local/env python import os, sys nargs = len(sys.argv) if not 3 <= nargs <= 5: print &qu ...

  8. Java 在PDF中添加水印——文本/图片水印

    水印是一种十分常用的防伪手段,常用于各种文档.资料等.常见的水印,包括文字类型的水印.图片或logo类型的水印.以下Java示例,将分别使用insertTextWatermark(PdfPageBas ...

  9. java从pdf中提取文本

    一(单文件转换):下载pdfbox包,百度搜pdfbox.(fontbox-1.8.16.jar和pdfbox-app-1.8.16.jar) package pdf; import java.io. ...

随机推荐

  1. ASP.NET MVC 重命名[命名空间]而导致的错误及发现的ASP.NET MVC Bug一枚

    使用VS2012新建了一个Asp.net mvc5的项目,并把项目的命名空间名称更改了(Src更改为UXXXXX),然后就导致了以下错误 刚开始以后是项目的属性中的命名空间没有更改过来的问题,但我在重 ...

  2. linux性能分析工具集(图示)

  3. 通读cheerio API

    所谓工欲善其事,必先利其器,所以通读了cheerio的API,顺便翻译了一遍,有些地方因为知道的比较少,不知道什么意思,保留了英文,希望各位不吝告诉我,然后一起把这个翻译完成. ###cheerio ...

  4. [Spring Boot] @Component, @AutoWired and @Primary

    Spring boot is really good for Dependencies injection by using Autowiring. Each class instancse in s ...

  5. 公共Maven库

    <repository><id>codelds</id><url>https://code.lds.org/nexus/content/groups/m ...

  6. spring MVC、mybatis配置读写分离,ReplicationDriver(转载)

    参考:http://shift-alt-ctrl.iteye.com/blog/2271730c 环境: 3台数据库机器,一个master,二台slave,分别为slave1,slave2 2.要实现 ...

  7. iOS NSNotificationCenter 最基本使用

    NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:] , @"a ...

  8. 用javascript请求动态页url返回更新

    例如我们用Ajax请求一个动态页返回的信息,或一个图片验证码请求一个*.ashx页面,第一次请求没问题,而第二次请求时,不变化,为啥? 因为第二次及以后请求的url与第一次是一样的,所以服务器(或是浏 ...

  9. Hessian 原理分析

    Hessian 原理分析 一.远程通讯协议的基本原理 网络通信需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络 IO 来实现,其中传输协议比较出名的有 http . tcp . u ...

  10. css布局 三栏 自动换行

    1.代码实现 <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UT ...