如何使用 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 很慢。

然后我没有想法/有其他事情要做。