まず最初に、まともなPDFライブラリを使用できるとしたらどれほど簡単かをお見せしましょう。私は例としてiTextSharpを使用しますが、PDFBoxやPDFNet(@Ikaが彼の回答ですでに言及している)のような他のものでも同じことができます。
PdfReader reader = new PdfReader(sourcePdf);
using (PdfStamper stamper = new PdfStamper(reader, targetPdfStream)) {
Font FONT = new Font(Font.FontFamily.HELVETICA, 12, Font.BOLD, new GrayColor(0.75f));
PdfContentByte canvas = stamper.GetOverContent(1);
ColumnText.ShowTextAligned(
canvas,
Element.ALIGN_LEFT,
new Phrase("Hello people!", FONT),
36, 540, 0
);
}
( Web化されたiTextSharpの例 StampText.csから派生し、iText in Action — 2nd Editionの第6章で説明されています。)
(どのPDFライブラリを選択するかは、一般的な要件と使用可能なライセンスモデルによって異なります。)
このようなPDFライブラリの使いやすさにもかかわらず、手動で行うことを主張する場合は、ここにいくつかの注意事項があります。
まず、コンテンツを追加するページのページ辞書を見つける必要があります。PDFのタイプによっては、オブジェクトストリームなどの解凍がすでに必要になる場合がありますが、サンプルのmodified1.pdfでは必要ありません。
7 0 obj
<</Rotate 90
/Type /Page
/TrimBox [ 9.54 6.12 585.68 835.88 ]
/Resources 8 0 R
/CropBox [ 0 0 595.22 842 ]
/ArtBox [ 9.54 18.36 585.68 842 ]
/Contents [ 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R ]
/Parent 6 0 R
/MediaBox [ 0 0 595.22 842 ]
/Annots 17 0 R
/BleedBox [ 9.54 6.12 585.68 835.88 ]
>>
endobj
コンテンツストリームへの参照の配列が表示されます。ここに、新しいページコンテンツを追加する必要があります。既存のストリームを操作するか、新しいストリームを作成してそのアレイに追加できます。
(ほとんどのPDFではコンテンツストリームが圧縮されています。したがって、一般的なケースでは、作業を開始する前にストリームを解凍する必要があります。したがって、私の目には、新しいストリームを開始する方が簡単です。)
PDFで圧縮されていない最後に参照されたストリーム160を操作することを選択しました。
16 0 obj
<</Length 37 0 R>>
stream
S 1 0 0 1 13.183 0 cm 0 0 m
[...]
0 10 -10 -0 506.238 342.629 Tm
.13333 .11765 .12157 scn
-.0002 Tc
.0006 Tw
(the Bank and branch on which cheque is drawn\).)Tj
/F1 2 Tf
-15.1279 10.9462 Td
(abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~!@#$%^&*aaaaaaaaaaaaa)Tj
/F2 1 Tf
015.1279 01.9462 Td
(ANAabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789)Tj
ET
endstream
endobj
私が集めたあなたの追加は、最初にフォントを選択し、次に挿入ポイントを配置し、最後に選択した文字を印刷する、下部にある2つの3ライナーです。
ここで、テストのためだけにテキストabc..zとABC...Zを追加したと言います。しかし、pdfに表示されていない文字bjkqvなど。2回目の文字の追加で、問題がさらに明らかになります。ここでは、大文字の「A」と「N」のみが表示されます。

これは、問題のフォントがPDFに埋め込まれているという事実によるものです---フォントはPDFに埋め込まれているため、問題のフォントを持たないシステムのPDFビューアは、PDFを表示できます---しかし、完全に埋め込まれているわけではなく、そのフォントに必要な文字のサブセットのみです。
「N」と「A」のみが表示されるフォントF2を探してみましょう。
ページオブジェクトによると、ページリソースはオブジェクト80にあります。
8 0 obj
<</Font <</F1 45 0 R /TT2 46 0 R /F2 47 0 R>>
/ExtGState <</GS2 48 0 R>>
/ProcSet [ /PDF /Text ]
/ColorSpace <</Cs6 49 0 R>>
>>
endobj
したがって、F2は470で定義されます。
47 0 obj
<</Subtype /Type1
/Type /Font
/Widths [ 722 250 250 250 250 250 250 250 250 250 250 250 250 722 ]
/Encoding 52 0 R
/FirstChar 65
/FontDescriptor 53 0 R
/ToUnicode 54 0 R
/BaseFont /ILBPOB+TimesNewRomanPSMT-Bold
/LastChar 78
>>
endobj
参照されているToUnicodeマップ540には、次のように表示されます。
54 0 obj
<</Length 55 0 R>>stream
/CIDInit /ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo <<
/Registry (AAAAAA+F2+0) /Ordering (T1UV) /Supplement 0 >> def
/CMapName /AAAAAA+F2+0 def
/CMapType 2 def
1 begincodespacerange <41> <4e> endcodespacerange
2 beginbfchar
<41> <0041>
<4e> <004E>
endbfchar
endcmap CMapName currentdict /CMap defineresource pop end end
endstream
endobj
このマッピングでは、文字コード0x41'A'と0x4e'N'のみがマッピングされていることがわかります。
ドキュメントでは、フォントは金額テーブルのセルに「NA」を印刷するためにのみ使用され、それ以外の場合は使用されません。したがって、これらの2つの文字「N」と「A」のみが埋め込まれます。その結果、そのフォントを追加すると、これらの文字のみが出力されます。
したがって、ページにテキストを正常に追加するには、ページに関連付けられているフォントリソースで提供されているグリフを確認するか(およびそれらのグリフへの追加を制限する)、独自のフォントリソースを追加する必要があります。
エンコーディングでの文字の存在は、ここにあるほど簡単にはわかりません(ToUnicodeはオプションです)ので、独自のフォントリソースを追加することをお勧めします。PDF仕様ISO32000-1は、その方法を説明しています。
さらに、テキストのx軸とy軸の位置がPDFで正しく表示されていないと述べています。正確に何を意味するのかはわかりませんが、コンテンツストリームでは、軸のストレッチ、スキュー、回転、移動など、ページの座標系にアフィン変換を適用できることに注意してください。
元の座標系を使用し、追加時に適切な座標に依存しない場合は、 q演算子を含むページに初期コンテンツストリームを追加する必要があります(現在のグラフィックス状態をグラフィックス状態スタックに保存するため) 。 Q演算子を使用して、新しい最終コンテンツストリームで追加を開始します(スタックから最後に保存された状態を削除して現在の状態にすることにより、グラフィックスの状態を復元します)。
編集サンプルとして、上部にあるC#コードに相当するJavaを、追加モードをアクティブにして、 modified1.pdfに適用しました。その結果、次のオブジェクトが変更または追加されました。
ページオブジェクト70が更新されました。
7 0 obj
<</CropBox[0 0 595.22 842]
/Parent 6 0 R
/Contents[69 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R 70 0 R]
/Type/Page
/Resources<<
/ExtGState<</GS2 48 0 R>>
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
/ColorSpace<</Cs6 49 0 R>>
/Font<</F1 45 0 R/F2 47 0 R/TT2 46 0 R/Xi0 68 0 R>>
>>
/MediaBox[0 0 595.22 842]
/TrimBox[9.54 6.12 585.68 835.88]
/BleedBox[9.54 6.12 585.68 835.88]
/Annots 17 0 R
/ArtBox[9.54 18.36 585.68 842]
/Rotate 90
>>
endobj
以前のバージョンと比較すると、
- 2つの新しいコンテンツストリームが追加されました。最初は690、最後は700です。
- リソースはもはや間接的なオブジェクトではなく、代わりにここに直接含まれています。
- リソースには、680に新しいフォントリソースXi0が含まれています。
次に、追加されたオブジェクトを見てみましょう。
これは、680のXi0という名前のHelvetica-Boldのフォントリソースです。
68 0 obj
<</BaseFont/Helvetica-Bold
/Type/Font
/Encoding/WinAnsiEncoding
/Subtype/Type1
>>
endobj
埋め込まれていない、標準の14フォントリソースはまったく複雑ではありません...
これで、追加のコンテンツストリームがあります。iTextはそれらを圧縮しますが、ここでは非圧縮状態で表示します。
69 0 obj
<</Length 1>>stream
q
endstream
endobj
70 0 obj
<</Length 106>>stream
Q
q
0 1 -1 0 595.22 0 cm
q
BT
1 0 0 1 36 540 Tm
/Xi0 12 Tf
0.75 g
(Hello people!)Tj
0 g
ET
Q
Q
endstream
endobj
したがって、最初の新しいコンテンツストリームは現在のグラフィック状態を保存し、最後の新しいコンテンツストリームはその保存された状態を取得し、座標系、テキスト挿入の位置を変更し、フォント、フォントサイズ、塗りつぶしの色を選択して、最後に印刷します。文字列。