最近,我一直在研究一个程序,该程序可以将TeX生成的PDF转换为某种形式的文本,该文本保留一些语义上有意义的样式信息,例如下标和上标。

调试时,PDFTextStripper类似乎可能发生了非常异常的事情。

这是我的TeXUtil类,可以完成大部分工作。

import com.google.common.base.CharMatcher;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.util.Hashtable;
import java.util.Stack;

public class TeXUtil {
    private Stack<SSStatus> ssstatus;
    private boolean accentMode;
    private String fs;
    private boolean mathMode;
    private SymbolDB db;
    private Hashtable<String, String> maccDict;
    float endY;//Positions
    float endX;
    float Y;
    int height;//Height
    //boolean test;
    public TeXUtil() throws IOException {
        ssstatus = new Stack<SSStatus>();
        fs = "rm";
        accentMode = false;//as in the state of being right after an accent
        mathMode = false;
        db = new SymbolDB();
        maccDict = new Hashtable<String, String>();
        maccDict.put("\\vec","\\vec");
        maccDict.put("\\widehat","\\widehat");
        maccDict.put("\\widetilde","\\widetilde");
        maccDict.put("\\^","\\hat");
        maccDict.put("\\v","\\check");
        maccDict.put("\\u","\\breve");
        maccDict.put("\\`","\\grave");
        maccDict.put("\\~","\\tilde");
        maccDict.put("\\=","\\bar");
        maccDict.put("\\.","\\dot");
        maccDict.put("\\","\\ddot");
        maccDict.put("\\'","\\acute");
        endY = 0;
        endX = 0;
        Y = 0;
        height = 0;
        //test = false;
        System.out.println("TeXUtil initialized!");
    }
    private static String fontShortName(PDFont font) {
        String[] segments = font.getName().split("\\+");
        return segments[segments.length - 1];
    }
    private static int fontHeight(PDFont font) {
        CharMatcher matcher = CharMatcher.inRange('0', '9');
        return Integer.parseInt(matcher.retainFrom(fontShortName(font)));
    }
    private static String fontClass(PDFont font) {
        CharMatcher matcher = CharMatcher.inRange('A', 'Z');
        return (matcher.retainFrom(fontShortName(font))).toLowerCase();
    }
    private String textToTeX(String shortFontName, int code) throws JSONException {
        JSONObject info = db.getInfo(shortFontName, code);
        return info.getString("value");
    }
    public String fullTextToTeX(PDFont font, int code, float newEndX, float newY, float newEndY){
        String shortFontName = fontClass(font);
        try {
            JSONObject info = db.getInfo(shortFontName, code);
            String teXCode = info.getString("value");
            StringBuilder preamble1 = new StringBuilder("");
            StringBuilder preamble2 = new StringBuilder("");
            StringBuilder postamble = new StringBuilder("");
            boolean text = info.getBoolean("text");
            boolean math = info.getBoolean("math");
            boolean tacc = info.getBoolean("tacc");
            boolean macc = info.getBoolean("macc");
            String newFont = info.getString("font");
            int newHeight = fontHeight(font);
            //Font change, rm is seen as having no font
            if (!newFont.equals(fs)) {
                if (!fs.equals("rm"))
                    preamble1.insert(0, '}');
                if (!newFont.equals("rm")) {
                    preamble2.append('\\');
                    preamble2.append(newFont);
                    preamble2.append('{');
                }
                preamble1.insert(0,  " fs = " + fs + " nFs = " + newFont + "\n");
                fs = newFont;
            }
            if (height == 0) {
                //preamble2.append(" Meow! am = " + accentMode + " fs = " + fs + " mm = " + mathMode + "\n");
            }
            //Subscripts/Superscripts
            if (height > newHeight && newEndX > endX) {//New subscript/superscript
                if (newEndY < endY) {//New superscript
                    //ssstatus.push(SSStatus.SUP);
                    preamble2.insert(0, "^{");
                }
                else if (newY > Y) {//New subscript
                    //ssstatus.push(SSStatus.SUB);
                    preamble2.insert(0, "_{");
                }
                //else {
                  //  System.out.println("Please investigate the situation: texcode = " + teXCode + "endY = " + endY + " Y=" + Y + " endX=" + endX + " newEndY=" + newEndY + " newY=" + newY + " newEndX= " + newEndX);
                //}
            }
            else if (height < newHeight && height != 0) {
                //ssstatus.pop();
                preamble1.append('}');
            }
            height = newHeight;
            endX = newEndX;
            endY = newEndY;
            Y = newY;
            //Enter or leave math mode
            if (mathMode && !math && !macc) {
                mathMode = false;
                preamble1.append('$');
            }
            else if (!mathMode && !text && !tacc) {
                mathMode = true;
                preamble2.insert(0,'$');
            }
            //Accents
            if (accentMode) {//If accent mode is ever entered we need to leave it at once
                postamble.append('}');
                accentMode = false;
            }
            if ((mathMode && macc) || (!mathMode && tacc)) {//Right now assume that anything that can be an accent is an accent
                postamble.append('{');
                if (mathMode)
                    teXCode = maccDict.get(teXCode);
                accentMode = true;
            }
            if (teXCode.charAt(0) == '\\')
                return preamble1.toString() + preamble2.toString() + teXCode + ' ' + postamble.toString();
            else
                return preamble1.toString() + preamble2.toString() + teXCode + postamble.toString();
        }
        catch(JSONException e) {
            return "\\" + shortFontName + "{" + code + "}";
        }
    }
}


这是主要的课程。

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.TextPosition;
import com.google.common.base.CharMatcher;
import org.json.*;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Stack;

public class TEX2TXT {

    public static void main(String args[]) throws IOException {
        TeXUtil util = new TeXUtil();
        //Loading an existing document
        File file = new File("/Users/CatLover/Documents/Tex/Examples/c4.pdf");
        PDDocument document = PDDocument.load(file);
        //Instantiate PDFTextStripper class
        PDFTextStripper pdfStripper = new PDFTextStripper() {
            protected void writeString(String text, List<TextPosition> textPositions) throws IOException {
                TeXUtil util = new TeXUtil();
                StringBuilder builder = new StringBuilder();
                for(TextPosition position: textPositions) {
                    float Y = position.getY();
                    float endY = position.getEndY();
                    float endX = position.getEndX();
                    PDFont font = position.getFont();
                    int[] codes = position.getCharacterCodes();
                    for(int code: codes) {
                        builder.append(util.fullTextToTeX(font, code, endX, Y, endY));
                    }

                }
                writeString(builder.toString());
            }
        };
        //Retrieving text from PDF document
        String text = pdfStripper.getText(document);
        System.out.println(text);
        //Closing the document
        document.close();
    }


真正奇怪的是,每次出现单词之间的任何空白时,都会构造TeXUtil,而TeXUtil()仅应调用一次。我不确定为什么会这样。由于PDF是由LaTeX生成的,因此LaTeX不会在PDF中放置空格字符,而是在字符之间保留空格以隐式表示空格,这可能会影响PDFBox的工作方式。

最佳答案

您正在TeXUtil子类的PDFTextStripper方法的第一行中构造一个新的writeString。如果仅删除该行,它仍应能够引用main方法中定义的util(尽管取决于所使用的Java版本,您可能必须将其设置为final)。

07-26 05:15