如何使用 pdfclown 提高搜索关键字在文件上突出显示的性能
How to improve performance for search keywords highlighting on file using pdfclown
我正在使用 pdfclown,下面的代码大约需要 100 秒来突出显示相同的搜索关键字 file.Kindly 请在下面提供您的意见以提高性能 code.Please 在下面找到 jar 路径 url 到 运行 这个代码。
https://drive.google.com/drive/folders/1nW8bk6bcAG6g7LZYy2YAAMk46hI9IPUh
import java.awt.Color;
import java.awt.Desktop;
import java.awt.geom.Rectangle2D;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.io.File;
import org.pdfclown.documents.Page;
import org.pdfclown.documents.contents.ITextString;
import org.pdfclown.documents.contents.TextChar;
import org.pdfclown.documents.contents.colorSpaces.DeviceRGBColor;
import org.pdfclown.documents.interaction.annotations.TextMarkup;
import org.pdfclown.documents.interaction.annotations.TextMarkup.MarkupTypeEnum;
import org.pdfclown.files.SerializationModeEnum;
import org.pdfclown.util.math.Interval;
import org.pdfclown.util.math.geom.Quad;
import org.pdfclown.tools.TextExtractor;
public class pdfclown2 {
private static int count;
public static void main(String[] args) throws IOException {
highlight("book.pdf","C:\Users\\Downloads\6.pdf");
System.out.println("OK");
}
private static void highlight(String inputPath, String outputPath) throws IOException {
URL url = new URL(inputPath);
InputStream in = url.openStream();
org.pdfclown.files.File file = null;
//"C:\Users\Desktop\pdf\80743064.pdf"
try {
file = new org.pdfclown.files.File("C:\Users\uc23\Desktop\pdf\80743064.pdf);
Map<String, String> m = new HashMap<String, String>();
for(int i=0;i<3500;i++){
if(i<=2){
m.put("The","hi");
m.put("know","hello");
m.put("is","Welcome");
}else{
m.put(""+i,"hi");
}
}
System.out.println("map size"+m.size());
long startTime = System.currentTimeMillis();
for (Map.Entry<String, String> entry : m.entrySet()) {
Pattern pattern;
String serachKey = entry.getKey().toLowerCase();
final String translationKeyword = entry.getValue();
if ((serachKey.contains(")") && serachKey.contains("("))
|| (serachKey.contains("(") && !serachKey.contains(")"))
|| (serachKey.contains(")") && !serachKey.contains("(")) || serachKey.contains("?")
|| serachKey.contains("*") || serachKey.contains("+")) {
pattern = Pattern.compile(Pattern.quote(serachKey), Pattern.CASE_INSENSITIVE);
}
else
pattern = Pattern.compile( "\b"+serachKey+"\b", Pattern.CASE_INSENSITIVE);
// 2. Iterating through the document pages...
TextExtractor textExtractor = new TextExtractor(true, true);
for (final Page page : file.getDocument().getPages()) {
// 2.1. Extract the page text!
Map<Rectangle2D, List<ITextString>> textStrings = textExtractor.extract(page);
//System.out.println(textStrings.toString().indexOf(entry.getKey()));
// 2.2. Find the text pattern matches!
final Matcher matcher = pattern.matcher(TextExtractor.toString(textStrings).toLowerCase());
// 2.3. Highlight the text pattern matches!
textExtractor.filter(textStrings, new TextExtractor.IIntervalFilter() {
public boolean hasNext() {
// System.out.println(matcher.find());
// if(key.getMatchCriteria() == 1){
if (matcher.find()) {
return true;
}
/*
* } else if(key.getMatchCriteria() == 2) { if
* (matcher.hitEnd()) { count++; return true; } }
*/
return false;
}
public Interval<Integer> next() {
return new Interval<Integer>(matcher.start(), matcher.end());
}
public void process(Interval<Integer> interval, ITextString match) {
// Defining the highlight box of the text pattern
// match...
System.out.println(match);
List<Quad> highlightQuads = new ArrayList<Quad>();
{
Rectangle2D textBox = null;
for (TextChar textChar : match.getTextChars()) {
Rectangle2D textCharBox = textChar.getBox();
if (textBox == null) {
textBox = (Rectangle2D) textCharBox.clone();
} else {
if (textCharBox.getY() > textBox.getMaxY()) {
highlightQuads.add(Quad.get(textBox));
textBox = (Rectangle2D) textCharBox.clone();
} else {
textBox.add(textCharBox);
}
}
}
textBox.setRect(textBox.getX(), textBox.getY(), textBox.getWidth(), textBox.getHeight());
highlightQuads.add(Quad.get(textBox));
}
new TextMarkup(page, highlightQuads, translationKeyword, MarkupTypeEnum.Highlight);
}
public void remove() {
throw new UnsupportedOperationException();
}
});
}
}
SerializationModeEnum serializationMode = SerializationModeEnum.Incremental;
file.save(new java.io.File(outputPath), serializationMode);
System.out.println("file created");
long endTime = System.currentTimeMillis();
System.out.println("seconds take for execution is:"+(endTime-startTime)/1000);
} catch (Exception e) {
e.printStackTrace();
}
finally{
in.close();
}
}
}
我的猜测是process
是瓶颈,可以轻松测试(将代码注释掉)。测量时间。分析应用程序的好时机。
一个简单的启发式优化:将第一个和最后一个 TextChar 矩形用于一个衬里,并考虑字体上升和下降,可以创建整个矩形。那已经可以加快速度了。
可能存在替代方案。提出更具体的问题。
进一步改进:
InputStream in = url.openStream();
应该是
InputStream in = new BufferedInputStream(url.openStream());
乘法 searchKey.contains 可能是在循环之前声明的模式。
可以对原始突出显示代码执行相同的技术,但随后应添加 multi-line 支持,每行一个 Quad。
textExtractor 对每个页面重复使用,这似乎是最快的方法,但请尝试在页面循环中声明它。
我希望你能得到一个更具体的答案,尽管我对此表示怀疑,因此才有了这个答案。更好的做法是将慢速代码与整体隔离开来。但我理解整体性能提升的愿望。
一个不太精确但可能更快的高亮代码:
List<TextChar> textChars = match.getTextChars();
Rectangle2D firstRect = textChars.get(0).getBox();
Rectangle2D lastRect = textChars.get(textChars.size() - 1).getBox();
Rectangle2D rect = firstRect.createUnion(lastRect);
highlightQuads.add(Quad.get(rect));
其他评论后
看来瓶颈在别处。我的猜测是当时的文本提取:因此反转两个循环:
TextExtractor textExtractor = new TextExtractor(true, true);
for (final Page page : file.getDocument().getPages()) {
for (Map.Entry<String, String> entry : m.entrySet()) {
Pattern pattern;
String serachKey = entry.getKey().toLowerCase();
final String translationKeyword = entry.getValue();
if ((serachKey.contains(")") && serachKey.contains("("))
|| (serachKey.contains("(") && !serachKey.contains(")"))
|| (serachKey.contains(")") && !serachKey.contains("(")) || serachKey.contains("?")
|| serachKey.contains("*") || serachKey.contains("+")) {
pattern = Pattern.compile(Pattern.quote(serachKey), Pattern.CASE_INSENSITIVE);
}
else
pattern = Pattern.compile( "\b"+serachKey+"\b", Pattern.CASE_INSENSITIVE);
拥有 Pattern
的地图可能有意义,因为 Pattern.compile
很慢。
然后我没有想法/有其他事情要做。
我正在使用 pdfclown,下面的代码大约需要 100 秒来突出显示相同的搜索关键字 file.Kindly 请在下面提供您的意见以提高性能 code.Please 在下面找到 jar 路径 url 到 运行 这个代码。 https://drive.google.com/drive/folders/1nW8bk6bcAG6g7LZYy2YAAMk46hI9IPUh
import java.awt.Color;
import java.awt.Desktop;
import java.awt.geom.Rectangle2D;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.io.File;
import org.pdfclown.documents.Page;
import org.pdfclown.documents.contents.ITextString;
import org.pdfclown.documents.contents.TextChar;
import org.pdfclown.documents.contents.colorSpaces.DeviceRGBColor;
import org.pdfclown.documents.interaction.annotations.TextMarkup;
import org.pdfclown.documents.interaction.annotations.TextMarkup.MarkupTypeEnum;
import org.pdfclown.files.SerializationModeEnum;
import org.pdfclown.util.math.Interval;
import org.pdfclown.util.math.geom.Quad;
import org.pdfclown.tools.TextExtractor;
public class pdfclown2 {
private static int count;
public static void main(String[] args) throws IOException {
highlight("book.pdf","C:\Users\\Downloads\6.pdf");
System.out.println("OK");
}
private static void highlight(String inputPath, String outputPath) throws IOException {
URL url = new URL(inputPath);
InputStream in = url.openStream();
org.pdfclown.files.File file = null;
//"C:\Users\Desktop\pdf\80743064.pdf"
try {
file = new org.pdfclown.files.File("C:\Users\uc23\Desktop\pdf\80743064.pdf);
Map<String, String> m = new HashMap<String, String>();
for(int i=0;i<3500;i++){
if(i<=2){
m.put("The","hi");
m.put("know","hello");
m.put("is","Welcome");
}else{
m.put(""+i,"hi");
}
}
System.out.println("map size"+m.size());
long startTime = System.currentTimeMillis();
for (Map.Entry<String, String> entry : m.entrySet()) {
Pattern pattern;
String serachKey = entry.getKey().toLowerCase();
final String translationKeyword = entry.getValue();
if ((serachKey.contains(")") && serachKey.contains("("))
|| (serachKey.contains("(") && !serachKey.contains(")"))
|| (serachKey.contains(")") && !serachKey.contains("(")) || serachKey.contains("?")
|| serachKey.contains("*") || serachKey.contains("+")) {
pattern = Pattern.compile(Pattern.quote(serachKey), Pattern.CASE_INSENSITIVE);
}
else
pattern = Pattern.compile( "\b"+serachKey+"\b", Pattern.CASE_INSENSITIVE);
// 2. Iterating through the document pages...
TextExtractor textExtractor = new TextExtractor(true, true);
for (final Page page : file.getDocument().getPages()) {
// 2.1. Extract the page text!
Map<Rectangle2D, List<ITextString>> textStrings = textExtractor.extract(page);
//System.out.println(textStrings.toString().indexOf(entry.getKey()));
// 2.2. Find the text pattern matches!
final Matcher matcher = pattern.matcher(TextExtractor.toString(textStrings).toLowerCase());
// 2.3. Highlight the text pattern matches!
textExtractor.filter(textStrings, new TextExtractor.IIntervalFilter() {
public boolean hasNext() {
// System.out.println(matcher.find());
// if(key.getMatchCriteria() == 1){
if (matcher.find()) {
return true;
}
/*
* } else if(key.getMatchCriteria() == 2) { if
* (matcher.hitEnd()) { count++; return true; } }
*/
return false;
}
public Interval<Integer> next() {
return new Interval<Integer>(matcher.start(), matcher.end());
}
public void process(Interval<Integer> interval, ITextString match) {
// Defining the highlight box of the text pattern
// match...
System.out.println(match);
List<Quad> highlightQuads = new ArrayList<Quad>();
{
Rectangle2D textBox = null;
for (TextChar textChar : match.getTextChars()) {
Rectangle2D textCharBox = textChar.getBox();
if (textBox == null) {
textBox = (Rectangle2D) textCharBox.clone();
} else {
if (textCharBox.getY() > textBox.getMaxY()) {
highlightQuads.add(Quad.get(textBox));
textBox = (Rectangle2D) textCharBox.clone();
} else {
textBox.add(textCharBox);
}
}
}
textBox.setRect(textBox.getX(), textBox.getY(), textBox.getWidth(), textBox.getHeight());
highlightQuads.add(Quad.get(textBox));
}
new TextMarkup(page, highlightQuads, translationKeyword, MarkupTypeEnum.Highlight);
}
public void remove() {
throw new UnsupportedOperationException();
}
});
}
}
SerializationModeEnum serializationMode = SerializationModeEnum.Incremental;
file.save(new java.io.File(outputPath), serializationMode);
System.out.println("file created");
long endTime = System.currentTimeMillis();
System.out.println("seconds take for execution is:"+(endTime-startTime)/1000);
} catch (Exception e) {
e.printStackTrace();
}
finally{
in.close();
}
}
}
我的猜测是process
是瓶颈,可以轻松测试(将代码注释掉)。测量时间。分析应用程序的好时机。
一个简单的启发式优化:将第一个和最后一个 TextChar 矩形用于一个衬里,并考虑字体上升和下降,可以创建整个矩形。那已经可以加快速度了。
可能存在替代方案。提出更具体的问题。
进一步改进:
InputStream in = url.openStream();
应该是
InputStream in = new BufferedInputStream(url.openStream());
乘法 searchKey.contains 可能是在循环之前声明的模式。
可以对原始突出显示代码执行相同的技术,但随后应添加 multi-line 支持,每行一个 Quad。
textExtractor 对每个页面重复使用,这似乎是最快的方法,但请尝试在页面循环中声明它。
我希望你能得到一个更具体的答案,尽管我对此表示怀疑,因此才有了这个答案。更好的做法是将慢速代码与整体隔离开来。但我理解整体性能提升的愿望。
一个不太精确但可能更快的高亮代码:
List<TextChar> textChars = match.getTextChars();
Rectangle2D firstRect = textChars.get(0).getBox();
Rectangle2D lastRect = textChars.get(textChars.size() - 1).getBox();
Rectangle2D rect = firstRect.createUnion(lastRect);
highlightQuads.add(Quad.get(rect));
其他评论后
看来瓶颈在别处。我的猜测是当时的文本提取:因此反转两个循环:
TextExtractor textExtractor = new TextExtractor(true, true);
for (final Page page : file.getDocument().getPages()) {
for (Map.Entry<String, String> entry : m.entrySet()) {
Pattern pattern;
String serachKey = entry.getKey().toLowerCase();
final String translationKeyword = entry.getValue();
if ((serachKey.contains(")") && serachKey.contains("("))
|| (serachKey.contains("(") && !serachKey.contains(")"))
|| (serachKey.contains(")") && !serachKey.contains("(")) || serachKey.contains("?")
|| serachKey.contains("*") || serachKey.contains("+")) {
pattern = Pattern.compile(Pattern.quote(serachKey), Pattern.CASE_INSENSITIVE);
}
else
pattern = Pattern.compile( "\b"+serachKey+"\b", Pattern.CASE_INSENSITIVE);
拥有 Pattern
的地图可能有意义,因为 Pattern.compile
很慢。
然后我没有想法/有其他事情要做。