昨日に引き続き今日もPDFアレコレをバージョンアップ。変更点は以下の通り。
Ver2.03 2020/3/22
・捺印データとしてpng画像に対応した。
・捺印時にマウスカーソルにプレビューデータを表示するようにした。
(pngは指定したファイルのプレビュー。pdfは円だけで少々ずれる。)
pngで捺印する処理は以下の通りで、pdfの場合とちょっとちがう。
public bool AddImg(ref string file, int pno, string strImg, double rx, double ry)
{
double imgSizemm = 12.0; // 画像の印影は固定的な値として認識させる(画像のDPI値がいい加減だから)
string temp = uty.GetTempPDFName();
bool stat = true;
PdfReader src = new PdfReader(file);
try
{
int pagesCount = src.NumberOfPages;
if (0 < pno && pno <= pagesCount)
{
iTextSharp.text.Image image = iTextSharp.text.Image.GetInstance(strImg);
iTextSharp.text.Rectangle srcBox = src.GetPageSize(pno);
int rot = src.GetPageRotation(pno);
double pos_y = 0, pos_x = 0;
if (rot == 0 || rot == 180)
{
pos_y = srcBox.Height * ry;
pos_x = srcBox.Width * rx;
}
else // 90度または270度回転していると、高さ幅が逆になる
{
pos_y = srcBox.Width * ry;
pos_x = srcBox.Height * rx;
}
double imgSizePoint = uty.mm2point(imgSizemm);
pos_y -= imgSizePoint / 2.0; // 捺印用PDFは中央に捺印イメージがある前提なので、用紙サイズから半分引く
pos_x -= imgSizePoint / 2.0;
PdfStamper stamper = new PdfStamper(src, new FileStream(temp, FileMode.Create));
PdfContentByte cb = stamper.GetOverContent(pno);
image.ScaleAbsoluteHeight((float)imgSizePoint);
image.ScaleAbsoluteWidth((float)imgSizePoint);
image.SetAbsolutePosition((float)pos_x, (float)pos_y);
cb.AddImage(image);
stamper.Close();
}
else
{
stat = false;
MessageBox.Show("ページが範囲外");
}
}
catch (PdfException ex)
{
stat = false;
MessageBox.Show(string.Format("捺印に失敗({0})", ex.Message));
}
finally
{
src.Close();
if (stat)
{
uty.FileDelete(file);
file = temp;
}
}
return stat;
}
</pre>
画像ファイルにはDPI値(解像度)が入っていることもあるが、入っていないこともある。解像度があれば物理的な大きさ(つまり何ミリかってこと)がわかるから、印影を何ミリにするかって指定が不要になるんだけどねぇ。よって、解像度がない前提で印影は12ミリ固定とした。解像度があるかどうかで処理を分岐するのが今後の課題。
pngでもう一つ重要なのは透過ができること。捺印するんだから印影の背景は透明である必要がある。BitmapやJpegでは透過ができないから、透過ができるpngで実現することにした。印影は、<a href="http://www.hakusyu.com/webmtm/" target="_blank" rel="noopener noreferrer">Web認印</a>で作成できて、結果としてpdfとpngができる。pdfは透過できるんだけど、なにぶんサイズがデカい。印影用のフォントpdfに埋め込んでいるからだろう。そのデカさが問題で4MBはまずい。1つ捺印するごとに4MBずつ増加したんじゃ実用に向かない。だからpngなわけだけど、Web認印でできるpngは透過されていない。そこで、<a href="https://tojikomorin.sakura.ne.jp/inkan/" target="_blank" rel="noopener noreferrer">印鑑透過</a>が必要になる。透過されていない元画像をアップロードすると、透過された状態のpngが取得できる。これはすぐれもので、ちゃんと周囲の空白まで除去してくれている。
マウスカーソルに印影のプレビューを配置するコードも書いておく。pictureBoxのImageに画像データを読み込んで、タイマー処理でマウスカーソル位置にくっつけるというもの。MouseMoveだと遅くてしょうがないから、タイマー処理が吉。InterValを10ミリ秒にしても、マウスがサクサク動いてくれた。
<pre class="brush: c-sharp;">
private void ChangeCursor()
{
string ext = Path.GetExtension(m_strNtinFile);
if (string.Compare(ext, ".pdf", true) == 0 || string.Compare(ext, ".png", true) == 0)
{
timer1.Interval = 10;
timer1.Enabled = true;
pictureBox2.Visible = true;
pictureBox2.SizeMode = PictureBoxSizeMode.StretchImage;
if (string.Compare(ext, ".pdf", true) == 0)
{
Bitmap img = new Bitmap(100, 100);
Graphics g = Graphics.FromImage(img);
Pen p = new Pen(Color.Red, 5);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.DrawEllipse(p, 5, 5, 90, 90);
//g.FillRectangle(Brushes.Red, g.VisibleClipBounds);
p.Dispose();
g.Dispose();
pictureBox2.Image = img;
}
else if (string.Compare(ext, ".png", true) == 0)
pictureBox2.Image = Image.FromFile(m_strNtinFile);
double width = pictureBox1.Image.Width / m_papermm_width * m_imgSizemm;
double height = pictureBox1.Image.Height / m_papermm_height * m_imgSizemm;
m_CursorX = pictureBox2.Width = (int)width;
m_CursorY = pictureBox2.Height = (int)height;
}
}
private void ReverseCursor()
{
string ext = Path.GetExtension(m_strNtinFile);
if (string.Compare(ext, ".pdf", true) == 0 || string.Compare(ext, ".png", true) == 0)
{
timer1.Enabled = false;
pictureBox2.Visible = false;
}
}
private void timer1_Tick(object sender, EventArgs e)
{
// PictureBoxをカーソルの位置に動かす。カーソルの真ん中に画像が表示されるよう、
// カーソルを表示しているPictureBoxのサイズの二分の一をX,Yそれぞれ引く
Point Coordinate = this.PointToClient(Cursor.Position);
pictureBox2.Location = new Point(Coordinate.X - (m_CursorX / 2), Coordinate.Y - (m_CursorY / 2));
// 印影プレビューは相対的な大きさとする
double width = pictureBox1.Image.Width / m_papermm_width * m_imgSizemm;
double height = pictureBox1.Image.Height / m_papermm_height * m_imgSizemm;
pictureBox2.Width = (int)width;
pictureBox2.Height = (int)height;
}
pdfを指定した場合に、配置される印影が少しずれるのは、pdf内の中央に印影がないため。右上にずれているから、配置した点よりも右上に配置されてしまう。pdfだから容易に編集できない。空白を無視した領域の中央で認識させればいいんだろうが、現状pdfの用紙サイズでしか認識していない。pdfでオブジェクト範囲を認識できればいいのだができるだろうか。これも今後の課題。pdfで配置している限りは、元pdfのサイズ分増えてしまうという問題もある。pdfから透過pngを作るところまで自前でやってしまうのもありか。。pdfの画像変換だって、現状やっているんだからきっとできる。課題がどんどん出てきて、いいんだか悪いんだか。