直接说问题,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. 3D打印机切片与控制软件

    3D模型必须经由两个软件的处理来完成打印程序:切片与传送.切片软件会将模型细分成可以打印的薄度,然后计算其打印路径.3d打印机客户端软件再把这系列动作传送到硬件,并提供控制其他功能的控制介面.了解您的 ...

  2. 强制开启android webview debug模式使用Chrome inspect

    强制开启android webview debug模式使用Chrome inspect https://blog.csdn.net/zhulin2609/article/details/5143782 ...

  3. sublime同步文件与siderbar

    有时候,打开了sider bar,想和Eclipse.idea一样,每打开一个tab,就可以在左侧的sider bar 目录上面看到我当前的位置,于是找到了一个插件. SyncedSideBar 安装 ...

  4. 【python】理想论坛爬虫1.08

    #------------------------------------------------------------------------------------ # 理想论坛爬虫1.08, ...

  5. (C++)关于拷贝构造函数 Copy Constructor

    题目: In which of the following scenarios is a Copy Constructor called or invoked? A.    When no conve ...

  6. Android 开源项目 eoe 社区 Android 客户端

    本文内容 环境 开源项目 eoe 社区 Android 客户端 本文介绍 eoe 社区 Android 客户端.它是一个开源项目,功能相对简单,采用侧边菜单栏.可以学习一下.点击此处查看 GitHub ...

  7. redis-dev

    redis install by centos   -------------------------------------------------------------------------- ...

  8. 通过项目逐步深入了解Mybatis<一>

    Mybatis Mybatis 和 SpringMVC 通过订单商品案例驱动 官方中文地址:http://www.mybatis.org/mybatis-3/zh/ 官方托管地址:https://gi ...

  9. ArcGIS查找空洞多边形

    现需要用ArcGIS将多边形面层中是"空洞"的要素查找出来. 代码思路 一开始没有思路,于是写了代码,基本流程如下: 1)遍历需要判断的要素(可通过属性筛选): 2)检查某一要素相 ...

  10. CSS外框高度自动适应

    当有浮动float时,最外框会不跟随内容的高度而高: 解决办法一:清除浮动  clear:both <!DOCTYPE html> <html xmlns="http:// ...