對現有docx/pdf檔案轉pdf,並新增超連結
需求:上傳一個檔案(可能關聯絡統中已有的公文),要求能自動識別並替換為超連結,顯示在網頁中。
目標:以PDF的形式進行展示,儘可能支援更多的檔案種類。
定義:以[公文名]的形式定義替換
實現:暫支援docx,對pdf支援不太友好,doc就涼涼
首先,我們先獲取我們需要處理的物件(檔案的上傳以及型別的限制就不多說了):
String fileInPath = "這裡應該是前臺上傳檔案的路徑";
String fileOutPath = "這裡應該是最終存放檔案的路徑";
//這是我們需要替換的公文及其連結鍵值對
Map<String, String> gwMap = new HashMap<String, String>();
for (GwFileInfo gwFileInfo : gwFileDaoS.getNormalGwFileList()) {
gwMap.put(gwFileInfo.getFile_name(), gwFileInfo.getOponer_path());
}
其次,判定檔案型別,進行操作
pdf如下
FileInputStream is = null;
File file = new File(fileInPath);
file.getParentFile().mkdirs();
PdfReader reader;
try {
reader = new PdfReader(fileInPath);
PdfStamper stamper;
stamper = new PdfStamper(reader, new FileOutputStream(fileOutPath));
List<float[]> floatList = getKeyWords(fileInPath);//獲取所有出現檔案的座標
for (int i = 0; i < floatList.size(); i++) {
System.err.println(floatList.get(i)[0]);
System.err.println(floatList.get(i)[1]);
System.err.println(floatList.get(i)[2]);
System.err.println(floatList.get(i)[3]);
PdfContentByte canvas = stamper.getOverContent((int) floatList.get(i)[4]);//當前所處頁及層級
canvas.saveState();
canvas.setColorFill(BaseColor.WHITE);//設定覆蓋層北京顏色
canvas.rectangle(floatList.get(i)[0], floatList.get(i)[1]-2, floatList.get(i)[3], floatList.get(i)[2]);//覆蓋層,-2是圖形偏移量
canvas.fill();
canvas.restoreState();
canvas.beginText(); //開始在覆蓋層寫公文名字(應該在floatList中去除,沒有做)以及超連結樣式,這邊缺失下劃線需要補上
BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
Font font = new Font(bf, 12, Font.BOLD);
font.setColor(BaseColor.RED);
//設定字型和大小
canvas.setFontAndSize(bf, 12);
canvas.setColorFill(BaseColor.BLUE);
//設定字型的輸出位置
canvas.setTextMatrix(floatList.get(i)[0], floatList.get(i)[1]);
//要輸出的text
canvas.showText("多退少補" );
canvas.endText();
PdfContentByte overContent = stamper.getUnderContent(1);//設定超連結層級
Rectangle rectangle = new Rectangle(floatList.get(i)[0], floatList.get(i)[1], floatList.get(i)[0]+floatList.get(i)[3], floatList.get(i)[1]+floatList.get(i)[2]);//設定超連結區域(和上面的不一樣哦)
overContent.rectangle(rectangle);
PdfAnnotation annotation = PdfAnnotation.createLink(
stamper.getWriter(), rectangle, PdfAnnotation.HIGHLIGHT_INVERT,
new PdfAction("http://itextpdf.com/")
);
stamper.addAnnotation(annotation, (int) floatList.get(i)[4]);
}
stamper.close();
reader.close();
}catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//返回關鍵字所在的座標和頁數,注意pdf左邊取值是從左下角開始
private static List<float[]> getKeyWords(String filePath){
final List<float[]> arrays = new ArrayList<float[]>();
PdfReader pdfReader;
try{
pdfReader = new PdfReader(filePath);
int pageNum = pdfReader.getNumberOfPages();
PdfReaderContentParser pdfReaderContentParser = new PdfReaderContentParser(pdfReader);
// 下標從1開始
for (int i = 1; i <= pageNum; i++) {
final int finalI = i;
pdfReaderContentParser.processContent(i, new RenderListener() {
//此方法是監聽PDF裡的文字內容,有重複情況會都把座標和頁碼資訊都存入arrays裡
@Override
public void renderText(TextRenderInfo textRenderInfo) {
String text = textRenderInfo.getText(); // 整頁內容
System.err.println(textRenderInfo.getText());
if (null != text && text.contains("費用管理暫行辦法")) {
Rectangle2D.Float boundingRectange = textRenderInfo
.getBaseline().getBoundingRectange();
float [] resu = new float[5];
System.out.println("======="+text);
System.out.println("h:"+boundingRectange.getHeight());
System.out.println("w:"+boundingRectange.width);
System.out.println("centerX:"+boundingRectange.getCenterX());
System.out.println("centerY:"+boundingRectange.getCenterY());
System.out.println("x:"+boundingRectange.getX());
System.out.println("y:"+boundingRectange.getY());
System.out.println("maxX:"+boundingRectange.getMaxX());
System.out.println("maxY:"+boundingRectange.getMaxY());
System.out.println("minX:"+boundingRectange.getMinX());
System.out.println("minY:"+boundingRectange.getMinY());
resu[0] = (float)boundingRectange.getX();
resu[1] = (float)boundingRectange.getY();
resu[2] = (float)(boundingRectange.getHeight() == 0 ? 12 : boundingRectange.getHeight());
resu[3] = (float)boundingRectange.width;
resu[4] = finalI;
arrays.add(resu);
}
}
@Override
public void renderImage(ImageRenderInfo arg0)
{
}
@Override
public void endTextBlock()
{
}
@Override
public void beginTextBlock()
{
}
});
}
} catch (IOException e){
e.printStackTrace();
}
return arrays;
}
小結:無法識別pdf中文字大小,只能用resu[2] = (float)(boundingRectange.getHeight() == 0 ? 12 : boundingRectange.getHeight());來設定預設高度,無法識別pdf背景顏色,預設使用的white來設計,所以看上去可能還是不錯的,但不具備通用性。這邊的公文什麼的迴圈比較也沒做,需要使用的話需要自行修改部分程式碼!
docx如下
關鍵點:文字替換,增加超連結,docx轉pdf
try {
example(fileInPath,gwMap);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
doc2pdf(fileInPath, fileOutPath);
public void example(String inFile,Map<String, String> gwMap) throws Exception {
InputStream is = new FileInputStream(inFile);
XWPFDocument document = new XWPFDocument(OPCPackage.open(is));
this.replaceInPara(document, gwMap);
document.write(new FileOutputStream(inFile));
}
private void replaceInPara(XWPFDocument doc, Map<String, String> params) {
Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
XWPFParagraph para;
while (iterator.hasNext()) {
para = iterator.next();
this.replaceInPara(para, params);
}
}
//文字
private void replaceInPara(XWPFParagraph para, Map<String, String> params) {
List<XWPFRun> runs;
Matcher matcher;
String link = null;
if (this.matcher(para.getParagraphText()).find()) {
runs = para.getRuns();
for (int i=0; i<runs.size(); i++) {
XWPFRun run = runs.get(i);
String runText = run.toString();
matcher = this.matcher(runText);
if (matcher.find()) {
System.err.println(matcher);
System.err.println((matcher = this.matcher(runText)).find());
//下面這個迭代替換會造成死迴圈,我也不是太清楚
// while ((matcher = this.matcher(runText)).find()) {
// System.err.println(runText.substring(1, runText.length()-1));
// System.out.println(params.get(runText.substring(1, runText.length()-1)));
// link = matcher.replaceFirst(String.valueOf(params.get(matcher.group(1))));
// }
link = matcher.replaceFirst(String.valueOf(params.get(matcher.group(1))));
//直接呼叫XWPFRun的setText()方法設定文字時,在底層會重新建立一個XWPFRun,把文字附加在當前文字後面,
//所以我們不能直接設值,需要先刪除當前run,然後再自己手動插入一個新的run。
if(params.get(matcher.group(1))!=null){
para.removeRun(i);
appendExternalHyperlink("../../"+link, runText.substring(1, runText.length()-1), para);
}
// para.insertNewRun(i).setText(runText.substring(1, runText.length()-1));
}
}
}
}
private Matcher matcher(String str) {
Pattern pattern = Pattern.compile("\\[([^\\]]+)\\]", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(str);
return matcher;
}
//新增超連結
public static void appendExternalHyperlink(String url, String text,XWPFParagraph paragraph) {
// Add the link as External relationship
String id = paragraph.getDocument().getPackagePart().addExternalRelationship(url,XWPFRelation.HYPERLINK.getRelation()).getId();
// Append the link and bind it to the relationship
CTHyperlink cLink = paragraph.getCTP().addNewHyperlink();
cLink.setId(id);
// Create the linked text
CTText ctText = CTText.Factory.newInstance();
ctText.setStringValue(text);
CTR ctr = CTR.Factory.newInstance();
CTRPr rpr = ctr.addNewRPr();
//設定超連結樣式
CTColor color = CTColor.Factory.newInstance();
color.setVal("0000FF");
rpr.setColor(color);
rpr.addNewU().setVal(STUnderline.SINGLE);
// //設定字型
// CTFonts fonts = rpr.isSetRFonts() ? rpr.getRFonts() : rpr.addNewRFonts();
// fonts.setAscii("微軟雅黑");
// fonts.setEastAsia("微軟雅黑");
// fonts.setHAnsi("微軟雅黑");
// //設定字型大小
// CTHpsMeasure sz = rpr.isSetSz() ? rpr.getSz() : rpr.addNewSz();
// sz.setVal(new BigInteger("24"));
ctr.setTArray(new CTText[] { ctText });
// Insert the linked text into the link
cLink.setRArray(new CTR[] { ctr });
// //設定段落居中
// paragraph.setAlignment(ParagraphAlignment.CENTER);
// paragraph.setVerticalAlignment(TextAlignment.CENTER);
}
public void doc2pdf(String inPath, String outPath) {
if (!getLicense()) { // 驗證License 若不驗證則轉化出的pdf文件會有水印產生
return;
}
try {
long old = System.currentTimeMillis();
File file = new File(outPath); // 新建一個空白pdf文件
FileOutputStream os = new FileOutputStream(file);
Document doc = new Document(inPath); // Address是將要被轉化的word文件
doc.save(os, SaveFormat.PDF);// 全面支援DOC, DOCX, OOXML, RTF HTML, OpenDocument, PDF,
// EPUB, XPS, SWF 相互轉換
long now = System.currentTimeMillis();
System.out.println("共耗時:" + ((now - old) / 1000.0) + "秒"); // 轉化用時
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
小結:這邊唯一存在的問題是[檔名]在建立的時候要是整體的形式,要不識別出來是
[
公文名
],這樣無法識別替換。
doc我就不贅述了,只做了個文字替換,無法直接新增超連結,其實可以互相轉換之後再實現,這是後話
jar包:我還是列出來吧,這個東西emmm,可能也是相當的坑的!
itextpdf-5.5.13.jar itext5處理核心
itext-asian-5.2.0.jar 用於支援pdf新增中文
aspose-words-15.8.0-jdk16.jar doc/docx等檔案轉pdf
還有就是poi有關jar包
一個License
<License>
<Data>
<Products>
<Product>Aspose.Total for Java</Product>
<Product>Aspose.Words for Java</Product>
</Products>
<EditionType>Enterprise</EditionType>
<SubscriptionExpiry>20991231</SubscriptionExpiry>
<LicenseExpiry>20991231</LicenseExpiry>
<SerialNumber>8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7</SerialNumber>
</Data>
<Signature>sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU=</Signature>
</License>
總結:這個功能不能算完成了吧,但是也做的7788的了,參考了很多網上的資料,然後組合起來,也是挺不容易的,也看到了很多提一樣的問題的人,好像很多都沒有一個好的解決方案。查資料的時候看見一句簽名,不僅是索取者,也是分享者,與大家共勉。
題外話:然後又有新需求啦,把一個文件拆成好幾個文件,emmm,只知道用docx4j.jar,還需要慢慢研究,今天就到這裡了!