9.1.2 抽出する文字に含まれた特殊なスペースを半角スペースに置き換える

ExtTextWithRplcSpecialSpaceToSpace_Top

狙い・効果

PDFから文字抽出抽出する文字列に含まれた特殊なスペースを半角スペースに置き換えます。

処理の概要

第1項では全角スペースを半角スペースに置き換えましたが、『PDF Tool API』はUnicodeのU+2000~U+200Bから、主に“Spaces”カテゴリに含まれる特殊スペースを置き換え対象にすることができます。(表9.3)

PtlParamExtractText.setUnicodeToSpace()でAHEXTRACTTEXT_UNI_SPACESを対象にすることで、抽出した文字列に含まれた特殊スペースを半角スペースに置き換えます。置き換える特殊文字はint型フラグで指定します。

表9.3 UnicodeのU+2000~U+200Bの“Spaces”の一覧
Unicode
表示名
内容
U+2000
En Quad
en幅(文字の高さの半分)のスペース。一般にU+2002と同等に扱われる。(U+2002が推奨)
U+2001
Em Quad
em幅(文字の高さと同等)のスペース。一般にU+2003と同等に扱われる。(U+2003が推奨)
U+2002
En Space
en幅のスペース。一般にU+2000と同等だがこちらが推奨される。
U+2003
Em Space
em幅のスペース。一般にU+2001と同等だがこちらが推奨される。
U+2004
Three-Per-Em Space
em幅の1/3のスペース。“thick space” の名称で知られる。
U+2005
Four-Per-Em Space
em幅の1/4のスペース。“mid space” の名称で知られる。
U+2006
Six-Per-Em Space
em幅の1/6のスペース。U+2009と同等に扱われることがある。
U+2007
Figure Space
数字1つの幅に等しいスペースで、フォントにより幅が変化することがある。別名“numeric space”
U+2008
Punctuation Space
ピリオド、またはカンマの送り幅に等しいスペース。
U+2009
Thin Space
em幅の1/5, または1/6のスペース。千、百万といった数値を千ずつで区切る際の文字として用いられることがある。
U+200A
Hair Space
Thin Space(U+2009)よりも狭い空白。
U+200B
Zero Width Space
ゼロ幅のスペース。単語間で目に見えるスペースがない言語などで単語の区切りをマークされるために用いられる。

サンプルプログラムでは、入力PDFの指定したページ全体から文字列を抜き出す際に上記の特殊スペースに加え、ノーブレークスペース、全角スペースについてそれぞれ半角スペースに置換する/しないを切り替えた上で抽出します。

『PDF Tool API』の主な機能

プログラム例

package cookbook;

import java.io.BufferedWriter;
import java.io.PrintWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.charset.StandardCharsets;

import jp.co.antenna.ptl.*;

public class ExtTextWithRplcSpecialSpaceToSpace {

    // そのクラスのusageを表示する関数
    private static void printUsage() {
        System.out.println("usage: java ExtTextWithRplcSpecialSpaceToSpace in-pdf-file"
                         + " out-text-file page-to-extract rplc-space rplc-no-break-space"
                         + " rplc-spaces rplc-ideographic-space");
        System.out.println("--以下の各文字に対して、[0:スペースに置き換えない  1:置き換える]を選択--");
        System.out.println("rplc-space : スペース");
        System.out.println("rplc-no-break-space : ノーブレークスペース");
        System.out.println("rplc-spaces : 特殊スペース");
        System.out.println("rplc-ideographic-space : 全角スペース");
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        if (args.length < 7) {
            printUsage(); // usageメッセージの表示
            return;
        }

        // コマンドライン引数の読み取り・判定
        // 出力PDFの名前はあとで渡すためにString型で保存する。
        String outputTextURI = args[1];
        int pageToExtract = Integer.parseInt(args[2]);
        boolean rplcSpace = false;
        boolean rplcNoBreakSpace = false;
        boolean rplcSpaces = false;
        boolean rplcIdeographicSpace = false;
        int unicodeCharFlag = 0;

        //各種削除フラグの読み取り判定
        //rplc-space
        try {
            rplcSpace = readBoolArgs(args[3], "rplc-spaceは" +
                                           "0か1で指定してください。");
        }
        catch (IllegalArgumentException ex) {
            System.out.println(ex.getMessage());
            printUsage(); // usageメッセージの表示
            return;
        }
        //rplc-no-break-space
        try {
            rplcNoBreakSpace = readBoolArgs(args[4], "rplc-no-break-spaceは" +
                                           "0か1で指定してください。");
        }
        catch (IllegalArgumentException ex) {
            System.out.println(ex.getMessage());
            printUsage(); // usageメッセージの表示
            return;
        }
        //rplc-spaces
        try {
            rplcSpaces = readBoolArgs(args[5], "rplc-spacesは" +
                                              "0か1で指定してください。");
        }
        catch (IllegalArgumentException ex) {
            System.out.println(ex.getMessage());
            printUsage(); // usageメッセージの表示
            return;
        }
        //rplc-ideographic-space
        try {
            rplcIdeographicSpace = readBoolArgs(args[6],
                                              "rplc-ideographic-spaceは" +
                                              "0か1で指定してください。");
        }
        catch (IllegalArgumentException ex) {
            System.out.println(ex.getMessage());
            printUsage(); // usageメッセージの表示
            return;
        }


        //各フラグの論理和をunicodeCharFlagに設定
        if(rplcSpace) {
            unicodeCharFlag = unicodeCharFlag | PtlParamExtractText.AHEXTRACTTEXT_UNI_SPACE;
        }
        if(rplcNoBreakSpace) {
            unicodeCharFlag = unicodeCharFlag | PtlParamExtractText.AHEXTRACTTEXT_UNI_NO_BREAK_SPACE ;
        }
        if(rplcSpaces) {
            unicodeCharFlag = unicodeCharFlag | PtlParamExtractText.AHEXTRACTTEXT_UNI_SPACES;
        }
        if(rplcIdeographicSpace) {
            unicodeCharFlag = unicodeCharFlag | PtlParamExtractText.AHEXTRACTTEXT_UNI_IDEOGRAPHIC_SPACE;
        }


        try(PtlParamInput inputFile = new PtlParamInput(args[0]);
             PtlPDFDocument doc = new PtlPDFDocument()) {
            // PDFファイルをロード
            doc.load(inputFile);
            try(PtlPages pages = doc.getPages()) {//ページコンテナの取得
                // ページコンテナが空かどうか
                if(pages.isEmpty()) {
                    System.out.println("ERROR : ページコンテナが空");
                    throw new Error("ERROR : ページコンテナが空");
                }
                //ページ数を取得
                int wholePageNum = doc.getPageCount();

                //pageToExtractが0ならすべてのページを処理する
                if(pageToExtract == 0) {
                    StringBuilder wholeTextFromPdf = new StringBuilder();
                    // ページの取得(パラメータindexは0が先頭のため1を引く)
                    for(int i = 0; i < wholePageNum; i++) {
                        try(PtlPage page = pages.get(i);
                            PtlContent content = page.getContent()) { // ページコンテントの取得
                            System.out.println((i+1)  + "ページ目のテキストを抽出します。");
                            wholeTextFromPdf.append(extractTextSetReplaceUnicodeFlag(unicodeCharFlag, content));
                        }
                    }
                    outputTextFile(outputTextURI, wholeTextFromPdf.toString());
                }else {
                    if(wholePageNum < pageToExtract) { //pageToExtractのエラー処理
	...【ExtTextWithRplcIdeographicSpaceToSpace.javaと同じ処理のため省略
	   ・総ページ数よりも大きいページ数を指定した場合のエラー処理】...
                    }
                    try(PtlPage page = pages.get(pageToExtract - 1); //指定したページを取得する
                        PtlContent content = page.getContent()) {
                         System.out.println(pageToExtract  + "ページ目のテキストを抽出します。");
                        outputTextFile(outputTextURI, extractTextSetReplaceUnicodeFlag(unicodeCharFlag, content));
                    }
                }
            }
        }
	...【GetPDFVersion.javaと同じ処理のため省略
	   ・エラーメッセージ処理と出力】...
    }

    private static String extractTextSetReplaceUnicodeFlag(int unicodeCharFlag, PtlContent content)
                        throws IOException, PtlException, Exception, Error {
        try(PtlParamExtractText paramExtractText = new PtlParamExtractText()) { // 文字抽出のパラメータクラス。
            //setUnicodeToRemoveをパラメータに設定
            paramExtractText.setUnicodeToSpace(unicodeCharFlag);
            // 文字列抽出
            String textFromPdf = content.extractText(paramExtractText);
            System.out.println(textFromPdf);

            // 抽出した文字列を返す
            return textFromPdf;
        }
    }

    /**
     * テキストファイルを出力するための関数。 
     * 出力エンコードはUTF-8を指定する。
     * 特に外部からの呼び出しを想定しないためprivateとする。
     * 
     * @param outputTextURI 出力ファイルのURI。
     * @param TextFromPdf 出力したいString型変数
     */
    private static void outputTextFile(String outputTextURI, String TextFromPdf){
	...【ExtractTextSetRect.javaと同じ処理のため省略
	   ・outputTextURIのパスにTextFromPDFの内容をUTF-8エンコーディングで出力する処理】...
    }


    /**
     * 0または1を入力されたargsにより、trueまたはfalseを返すメソッド。
     * 
     * @param args 与えられるコマンドライン引数。0または1でtrueまたはfalseを指定する。
     * @param errorMessage argsが0か1でなかった場合に出力されるエラーメッセージを指定する。
     * @return argsの数値を読み取った結果を戻す
     * @throws java.lang.IllegalArgumentException argsが0か1でなかった場合に発生。
     */
    public static boolean readBoolArgs(String args, String errorMessage)
        throws IllegalArgumentException {
	...【FixUpPDFASetSaveOption.javaと同じ処理のため省略
	   ・0または1を読み取り、boolean型のfalseまたはtrueを返す関数】...
    }
}

プログラムファイル名

ExtTextWithRplcSpecialSpaceToSpace.java

入出力操作の例

C:\samples>java cookbook.ExtTextWithRplcSpecialSpaceToSpace 
usage: java ExtTextWithRplcSpecialSpaceToSpace in-pdf-file out-text-file page-to-extract rplc-space rplc-no-break-space rplc-spaces rplc-ideographic-space
--以下の各文字に対して、[0:スペースに置き換えない  1:置き換える]を選択--
rplc-space : スペース
rplc-no-break-space : ノーブレークスペース
rplc-spaces : 特殊スペース
rplc-ideographic-space : 全角スペース

C:\samples>java cookbook.ExtTextWithRplcSpecialSpaceToSpace Special_letter_sample_specialSpaces.pdf Output_ExtTextWithRplcSpecialSpaceToSpace.txt 0 1 1 1 1 
1ページ目のテキストを抽出します。
特殊文字一覧SPACEspace(U0020) is here " ""The quick brown fox jumps overthe lazy dog"NO_BREAK_SPACENO-Break space(U00A0) is here" ""The quick brown fox jumps over the lazy dog"NO-Break space(nbsp) is here " ""The quick brown fox jumps over the lazy dog"Special SpacesEn Quad(U2000) is here" ""The quick brown fox jumps over the lazy dog"Em Quad(U2001) is here" ""The quick brown fox jumps over the lazy dog"En Space(U2002) is here" ""The quick brown fox jumps over the lazy dog"

[...後略...]

Output text URI :Output_ExtTextWithRplcSpecialSpaceToSpace.txt
-- 完了 --

この操作例では例文の空白がそれぞれの該当する特殊スペースになったPDFが用意されています。コマンドライン上でプログラムを実行した場合、コマンドラインの文字コードで表示できない特殊スペースは出力ログ上では「?」として表示されるときがあります。

5-9-1-2_ExtTextWithRplcSpecialSpaceToSpace_00

図9.4 特殊スペースを用意したPDF(抜粋)

次は特殊スペースをすべて半角スペースに置き換えた例と置き換えなかった例の比較です。

5-9-1-2_ExtTextWithRplcSpecialSpaceToSpace_01

図9.5 操作例の出力テキスト(抜粋)。特殊スペースをすべて半角スペースに置き換えている

5-9-1-2_ExtTextWithRplcSpecialSpaceToSpace_02

図9.6 特殊スペースをそのまま残した例(抜粋)。広いスペースや狭いスペースが混在している