ネットで探しても、あるのか無いのか。作ってみました。
あくまで基本的には、という温かい目で見てください。
簡単そうでなかなか・・・
とりあえず、ASCIIコード、つまり半角英文字と数字・記号が利用できる程度のもので、且つ"ABC"を'AAA'に置換する、という程度のものです。
PDFは漢字については、その文字コード(番号)を半角英数字で記述しています。(バイナリで0Aなら、半角数字で0Aと記述という具合です)
しかも、SJIS・UTF8は指定によりPDF文書ごとに・・・となっているようです。
サーチそのものはCONTENTSの中に出現する文字列をbyte配列の状態で比較しています。
ただ、このプログラムで取得されるのは、制御関係の半角英数字が混じっている長いbyte配列です。なので、制御系とマッチし てしまわないように5文字以上の長めの単語で使ってください。
ここから、制御の文字列ではない状態のケースのみで、マッチするか?という処理をする。というように改造していく感じだと思います。
/// <summary>指定のPDFに、指定の文字列があれば、指定のbyte(初期値は半角SP)で埋めます。</summary>
static private bool replace_PDF_textCONTENTS_proc(string in_in_filepath, string in_out_filepath, string in_moto_text,byte in_埋めるbyte ,int in_replace_count = 0)
{
if (File.Exists(in_in_filepath) == false)
return false;
byte[] tmp_sarch_bytes = System.Text.Encoding.UTF8.GetBytes(in_moto_text);
PdfReader reader = new PdfReader(in_in_filepath);
for (int i番 = 1; i番 <= reader.NumberOfPages; i番++)
{
PdfDictionary dict = reader.GetPageN(i番);
PdfObject object2 = dict.GetDirectObject(PdfName.CONTENTS);
if (object2 is PRStream)
{
PRStream stream = (PRStream)object2;
byte[] data = PdfReader.GetStreamBytes(stream);
int match_count = 0;
for (int i = 0; i < data.Length - tmp_sarch_bytes.Length; i++)
{
byte type_by = data[i];
Func<bool> get_match = () =>
{
for (int p = 0; p < tmp_sarch_bytes.Length; p++)
{
if (data[i + p] != tmp_sarch_bytes[p])
return false;
}
return true;
};
bool is_match = get_match();
if (is_match == false)
continue;
//
// 合致したケース
match_count++;
for (int p = 0; p < tmp_sarch_bytes.Length; p++)
{
data[i + p] = in_埋めるbyte;
}
if (in_replace_count != 0 && match_count == in_replace_count) // 置換の回数指定があれば、その回数の置換で終了します。
break;
}
stream.SetData(data);
}
}
try
{
PdfStamper stamper = new PdfStamper(reader, new FileStream(in_out_filepath, FileMode.Create));
stamper.Close();
reader.Close();
}
catch (Exception ex)
{
}
finally
{
reader.Close();
}
return true;
}
##以下のは、異なる長さに置換できるのですが、置換後を""としたところ問題がありました。
WindowsのPDFビュワーでは問題なく表示できます。
ですが、PDFダイレクト対応プリンタ(Ricoh MPC5503)ではエラーで印刷できませんでした。
上の"ABC"を"AAA"にする、置き換え型では大丈夫でした。
文字列が無い、というのが厳密なPDF仕様ではアウトなのかもしれません・・・
static private bool replace_PDF_textCONTENTS_proc(string in_in_filepath, string in_out_filepath, string in_moto_text,string in_new_text)
{
if (File.Exists(in_in_filepath) == false)
return false;
byte[] new_bytes = System.Text.Encoding.UTF8.GetBytes(in_new_text);
byte[] tmp_sarch_bytes = System.Text.Encoding.UTF8.GetBytes(in_moto_text);
PdfReader reader = new PdfReader(in_in_filepath);
for (int page番号 = 1; page番号 <= reader.NumberOfPages; page番号++) // このpage番号は1オリジンです
{
PdfDictionary dict = reader.GetPageN(page番号);
PdfObject object2 = dict.GetDirectObject(PdfName.CONTENTS);
if (object2 is PRStream)
{
PRStream stream = (PRStream)object2;
byte[] data = PdfReader.GetStreamBytes(stream);
List<byte> new_data = new List<byte>();
//
// 制御の文字列(オペレータ)は以下に詳しく書かれています。
// http://www.kobu.com/docs/pdf/pdfxhand.htm
string debug_text_UTF8 = System.Text.Encoding.UTF8.GetString(data);
string debug_text_Unicode = System.Text.Encoding.Unicode.GetString(data);
int match_count = 0;
for (int i = 0; i < data.Length - tmp_sarch_bytes.Length; i++)
{
byte type_by = data[i];
char type_c = (char)type_by;
Func<bool> get_match = () =>
{
for (int p = 0; p < tmp_sarch_bytes.Length; p++)
{
if (data[i + p] != tmp_sarch_bytes[p])
return false;
}
return true;
};
bool is_match = get_match();
if (is_match == false)
{
new_data.Add(data[i]);
continue;
}
//
// 合致したケース
match_count++;
new_data.AddRange(new_bytes);
i += tmp_sarch_bytes.Length;
}
//byte[] set_data = ;
stream.SetData(new_data.ToArray());
}
}
try
{
PdfStamper stamper = new PdfStamper(reader, new FileStream(in_out_filepath, FileMode.Create));
stamper.Close();
reader.Close();
}
catch(Exception ex)
{
}
reader.Close();
return true;
}