webdevqa.jp.net

Unicode PDF

私のプログラムは比較的単純なPDF文書を要求に応じて生成しますが、漢字や奇数の数学記号などのUnicode文字に問題があります。通常の文字列をPDFに書き込むには、括弧で囲みます。

(something)

8進コードで文字をエスケープするオプションもあります。

(\527)

ただし、これは512文字までです。より高い文字をどのようにエンコードまたはエスケープしますか?バイトストリームや16進エンコードされた文字列への参照を見てきましたが、私が読んだどの参照も、実際にそれを行う方法を喜んで教えてくれないようです。


編集:または、良いJava PDF私のために仕事をするライブラリです。現在使用しているのはgnujpdfのバージョン(元の作者がAWOLになっているように見えるため、いくつかのバグを修正しました)です。これにより、AWTグラフィックスインターフェイスに対してプログラムすることができます。

代替案は、HTML-> PDF、またはHTMLに非常によく似ている段落やボックスに基づくプログラムモデルのようです。 iTextは後者の例です。これは私の既存のコードを書き直すことを意味します、そして私はそれらが私に同じレイアウトの柔軟性を与えると確信していません。


編集2:気づかなかったが、iTextライブラリにはGraphics2D APIがあり、Unicodeを完全に処理しているように見えるので、これを使用します。質問に対する答えではありませんが、私にとっては問題を解決します。


編集3: iTextはうまく機能しています。レッスンは、無意味に困難に思われる何かに直面したときに、あなたよりもそれについて知っている人を探すことだと思います。

35
Marcus Downing

簡単な答えは、簡単な答えはないということです。 PDFの仕様を見ると、テキスト表示のメカニズムに特化した章全体が、そして長い章が1つあることがわかります。私は自社のPDFサポートをすべて実装し、テキストの処理はこれまでで最も複雑な演習の一部でした。 PDFファイルに対して非常に具体的で特別な目的の要件がない限り、発見したソリューション(サードパーティのライブラリを使用して作業を行う)は本当に最良の選択です。

12
Derek Clegg

第3章のPDF参照)では、Unicodeについて次のように述べています。

テキスト文字列は、PDFDocEncodingまたはUnicode文字エンコーディングでエンコードされます。 PDFDocEncodingは、ISO Latin 1エンコーディングのスーパーセットであり、付録Dに記載されています。Unicodeは、UnicodeコンソーシアムによるUnicode規格に記載されています(参考文献を参照)。 Unicodeでエンコードされたテキスト文字列の場合、最初の2バイトは254の後に255が続く必要があります。これらの2バイトは、UnicodeバイトオーダーマーカーU + FEFFを表し、文字列がUTF-16BE(ビッグエンディアン)エンコードスキームでエンコードされていることを示します。 Unicode標準で指定されています。 (このメカニズムは、PDFDocEncodingを使用して、2つの文字を含むイデレシスを含む文字列の開始を排除します。これは、単語またはフレーズの意味のある開始である可能性が低いです)。

35
plinth

アルゴマンの答えは、多くの点でwrongです。あなたはできますPDF Unicodeを含むドキュメントを作成し、それはロケットサイエンスではありませんが、それはいくつかの作業を必要とします。はい、彼は正しいです。使用するには1つのフォントで255文字を超える場合は、複合フォント(CIDFont)pdfオブジェクトを作成する必要があります。次に、使用する実際のTrueTypeフォントをCIDFontのDescendatFontエントリとして指定します。その後のトリックは、文字コードの代わりにフォントのグリフインデックス。このインデックスマップを取得するには、フォントのcmapセクションを解析する必要があります-GetFontData関数を使用して、TTF仕様を手に入れてください。

cmapセクションを解析するためのサンプルコードは次のとおりです: https://web.archive.org/web/20150329005245/http://support.Microsoft.com/en-us/kb/24102

そして、はい。@ user2373071が指摘した/ ToUnicodeエントリを忘れないでください。そうしないと、ユーザーはPDFを検索したり、そこからテキストをコピーしたりできなくなります。

10
dredkin

Dredkinが指摘したように、ページコンテンツストリームでは、Unicode文字値の代わりにグリフインデックスを使用する必要があります。 UnicodeテキストをPDFで表示するにはこれで十分ですが、Unicodeテキストは検索できません。テキストを検索可能にしたり、コピー/貼り付けしたりするには、/ ToUnicodeストリームも含める必要があります。このストリームは、ドキュメント内の各グリフを実際のUnicode文字に変換する必要があります。

5
user2373071

PDF仕様の付録D(995ページ)を参照してください。 PDFコンシューマアプリケーションで事前に定義されているフォントと文字セットの数は限られています。他の文字を表示するには、それらを含むフォントを埋め込む必要があります。ファイルサイズを小さくするために、必要な文字のみを含むフォントのサブセットのみを埋め込むことも推奨されます。 PDFでのUnicode文字の表示にも取り組んでおり、これは大きな手間です。

PDFBoxまたはiTextを確認してください。

http://www.Adobe.com/devnet/pdf/pdf_reference.html

4
jm4

私はこの問題に数日間取り組んできましたが、私が学んだことは、UnicodeはPDFでは不可能であるということです。 2バイト文字を使用すると、台座の記述方法はCIDフォントでのみ機能します。

一見、CID-Fontsはpdf-internalコンストラクトであり、その意味では実際にはフォントではありません-それらは(16ビットアドレスで)アドレス指定することによって呼び出すことができるグラフィックスサブルーチンのように見えます。

したがって、UnicodeをPDFで使用するには直接

  1. 通常のフォントをCID-Fontsに変換する必要があります。これはおそらく非常に困難です。元のフォントからグラフィックスルーチンを生成し(?)、文字メトリックなどを抽出する必要があります。
  2. 通常のフォントのようにCID-Fontを使用することはできません-通常のフォントをロードおよびスケーリングする方法でそれらをロードまたはスケーリングすることはできません
  3. また、2バイト文字はUnicodeスペース全体をカバーしていません

私見、これらの点により、ユニコードを直接使用することは絶対に不可能です。



代わりに私が今やっていることは、次の方法で文字を間接的に使用しています:すべてのフォントに対して、コードページ(および高速のルックアップテーブル)を生成しますlookups)-C++ではこれは次のようになります

std::map<std::string, std::vector<wchar_t> > Codepage;
std::map<std::string, std::map<wchar_t, int> > LookupTable;

次に、ページにunicode-stringを配置したいときはいつでも、その文字を反復処理し、それらをルックアップテーブルで検索し、新しい場合は、次のようにコードページに追加します。

for(std::wstring::const_iterator i = str.begin(); i != str.end(); i++)
{                
    if(LookupTable[fontname].find(*i) == LookupTable[fontname].end())
    {
        LookupTable[fontname][*i] = Codepage[fontname].size();
        Codepage[fontname].Push_back(*i);
    }
}

次に、新しい文字列を生成します。元の文字列の文字は、次のようにコードページ内の位置に置き換えられます。

static std::string hex = "0123456789ABCDEF";
std::string result = "<";
for(std::wstring::const_iterator i = str.begin(); i != str.end(); i++)
{                
    int id = LookupTable[fontname][*i] + 1;
    result += hex[(id & 0x00F0) >> 4];
    result += hex[(id & 0x000F)];
}
result += ">";

たとえば、「H€llo World!」 <01020303040506040703080905>になる可能性があります。これで、通常どおりTj演算子を使用して、その文字列をPDFに挿入して印刷することができます...

しかし、あなたは今問題を抱えています:PDFは01によって「H」を意味することを知りません。この問題を解決するには、pdfファイルにコードページも含める必要があります。これは、/ EncodingをFontオブジェクトに追加し、そのDifferences

「Hllllo World!」たとえば、次のFont-Objectは機能します。

5 0 obj 
<<
    /F1
    <<
        /Type /Font
        /Subtype /Type1
        /BaseFont /Times-Roman
        /Encoding
        <<
          /Type /Encoding
          /Differences [ 1 /H /Euro /l /o /space /W /r /d /exclam ]
        >>
    >> 
>>
endobj 

私はこのコードでそれを生成します:

ObjectOffsets.Push_back(stream->tellp()); // xrefs entry
(*stream) << ObjectCounter++ << " 0 obj \n<<\n";
int fontid = 1;
for(std::list<std::string>::iterator i = Fonts.begin(); i != Fonts.end(); i++)
{
    (*stream) << "  /F" << fontid++ << " << /Type /Font /Subtype /Type1 /BaseFont /" << *i;

    (*stream) << " /Encoding << /Type /Encoding /Differences [ 1 \n";
    for(std::vector<wchar_t>::iterator j = Codepage[*i].begin(); j != Codepage[*i].end(); j++)
        (*stream) << "    /" << GlyphName(*j) << "\n";
    (*stream) << "  ] >>";

    (*stream) << " >> \n";
}
(*stream) << ">>\n";
(*stream) << "endobj \n\n";

グローバルフォントレジスタを使用していることに注意してください-PDF文書全体で同じフォント名/ F1、/ F2、...を使用しています。すべてのページの/ Resourcesエントリで同じfont-registerオブジェクトが参照されます。これを別の方法で行う場合(たとえば、ページごとに1つのフォントレジスタを使用する場合)-コードを状況に合わせて調整する必要がある場合があります...

では、どのようにしてグリフの名前を見つけますか(「€」には/ Euro、「!」には/ exclamなど)。上記のコードでは、これは単に「GlyphName(* j)」を呼び出すことで行われます。私は次の場所にあるリストからBASHスクリプトを使用してこのメ​​ソッドを生成しました

http://www.jdawiseman.com/papers/trivia/character-entities.html

そしてそれはこのように見えます

const std::string GlyphName(wchar_t UnicodeCodepoint)
{
    switch(UnicodeCodepoint)
    {
        case 0x00A0: return "nonbreakingspace";
        case 0x00A1: return "exclamdown";
        case 0x00A2: return "cent";
        ...
    }
}

重大な問題私が開いたままにしていることは、これが最大254の異なる文字を使用する場合にのみ機能することです同じフォントから。 254を超える異なる文字を使用するには、同じフォントに対して複数のコードページを作成する必要があります。

PDF内では、さまざまなコードページがさまざまなフォントで表されているため、コードページを切り替えるには、フォントを切り替える必要があります。これにより、理論的にはPDFがかなり膨らむ可能性がありますが、私は1つでそれで問題ありません...

2
Algoman