CAD日記

主にAutoCADのことについて書いているけど、近頃は投資系ネタに注力している。自動売買、仮想通貨、PC関係、プログラミングなど。@caddiary

ソフト開発

PDFアレコレにページ挿入機能を追加

投稿日:2020年5月6日 更新日:

PDFアレコレのページ挿入機能とちょっとした不具合修正。
Ver2.08 2020/5/6
・ページ挿入に対応した。指定したページの前に、指定したPDFファイルまたは空白ページを挿入できる。指定ページに最終ページを指定すれば最後に追加できる。
・プロパティ画面にキャンセルボタンを追加した。(OKボタンだと常に更新していたため)
・PDFを編集した際、%Temp%フォルダに作業用のPDFが残っていたので削除するようにした。
詳細はコチラ。

ページ挿入のUIは以下の通り。

位置として指定したページの前に挿入することとして、挿入対象をファイルか空白ページとして指定する。
ファイルとすれば、OKボタンを押した後にPDFファイルを指定して挿入する。位置はコンボボックス形式として、内容は全ページ分の数値と最終ページ。初期値はプレビュー画面で表示していたページ。最終ページという選択を可能としたのがポイントで、これによりPDF内のどこにでもページを挿入できる仕様とした。
空白ページとすれば、内部処理で空白ページを作っている。空白ページの用紙サイズは、プレビュー画面で表示していたページのサイズとした。空のページを追加したいのはヤマヤマだったが、オブジェクトを何も追加しないと0バイトのPDFになってしまったので、何も見えないオブジェクトとしてArialの半角スペース文字を追加しておいた。

以下、iTextSharpをC#で組んでいる人でPDFページ挿入をしようという人に参考になるかと思って、ソースコードを公開しておく。
PDFの空ページを作る処理。空ページを作るために半角スペースの文字を入れているが、文字フォントが埋め込まれて20kbほどのサイズになるのが難点。空ページにするために何かをいれなきゃいけないのはいいとして、それを何にするのか。。今後検討の上、改善していこう。

private bool emptyPage(iTextSharp.text.Rectangle size, out string strEmp)
{
	bool stat = true;
	strEmp = null;
	try
	{
		using (Document docEmp = new Document(size))
		{
			using (MemoryStream memEmp = new MemoryStream())
			{
				using (PdfWriter writer = PdfWriter.GetInstance(docEmp, memEmp))
				{
					docEmp.Open();
					PdfContentByte pdfContentByte = writer.DirectContent;
					FontFactory.RegisterDirectories();
					var baseFont = FontFactory.GetFont("Arial", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED, 8f);
					pdfContentByte.BeginText();
					pdfContentByte.SetFontAndSize(baseFont.BaseFont, 8);
					pdfContentByte.ShowTextAligned(PdfContentByte.ALIGN_LEFT, " ", 100f, 100f, 0);
					pdfContentByte.EndText();
					docEmp.Close();
					strEmp = uty.GetTempPDFName();
					using (BinaryWriter w = new BinaryWriter(File.OpenWrite(strEmp)))
					{
						w.Write(memEmp.ToArray());
					}
				}
			}
		}
	}
	catch (PdfException ex)
	{
		stat = false;
		MessageBox.Show(string.Format("空ページ作成に失敗({0})", ex.Message));
	}
	finally
	{
		if (!stat && strEmp != null)
			uty.FileDelete(strEmp);
	}
	return stat;
}

ページ挿入処理。PdfCopyというクラスを使うのがポイント。挿入位置による分岐処理が美しくない。考え方として(selpageの値によって)3つのケースに分けたのはいいんだけど、そもそもケース分けは必要なかったか。元PDFの前半、挿入するページ、元PDFの後半というように3つのブロック分けて、selpageの値によって1つ目と3つ目のいずれかをやらないようにすればよい。

public int PageInsert(ref string file, int selpage, string inspath, int page)
{
	string temp = uty.GetTempPDFName();
	int stat = 1;  // 挿入したページの最後のページ
	PdfReader reader1 = new PdfReader(file);
	PdfReader reader2 = null;
	PdfReader reader3 = null;
	string emptyFile = null;
	if (inspath != null)
		reader2 = new PdfReader(inspath);
	else
	{
		if (emptyPage(reader1.GetPageSizeWithRotation(page), out emptyFile))
			reader3 = new PdfReader(emptyFile);
		else
			return 0;
	}
	try
	{
		using (Document document = new Document())
		{
			using (FileStream stream = new FileStream(temp, FileMode.Create, FileAccess.Write, FileShare.None))
			{
				using (PdfCopy copy = new PdfCopy(document, stream))
				{
					document.Open();
					if (selpage == 1)  // 先頭に挿入
					{
						if (reader2 != null)
						{
							for (int i = 1; i <= reader2.NumberOfPages; i++)
								copy.AddPage(copy.GetImportedPage(reader2, i));
							stat = reader2.NumberOfPages;
						}
						else
						{
							copy.AddPage(copy.GetImportedPage(reader3, 1));
							stat = 1;
						}
						for (int i = 1; i <= reader1.NumberOfPages; i++)
							copy.AddPage(copy.GetImportedPage(reader1, i));
					}
					else if (2 <= selpage && selpage <= reader1.NumberOfPages)  // 途中に挿入
					{
						for (int i = 1; i < selpage; i++)
							copy.AddPage(copy.GetImportedPage(reader1, i));
						if (reader2 != null)
						{
							for (int i = 1; i <= reader2.NumberOfPages; i++)
								copy.AddPage(copy.GetImportedPage(reader2, i));
							stat = selpage - 1 + reader2.NumberOfPages;
						}
						else
						{
							copy.AddPage(copy.GetImportedPage(reader3, 1));
							stat = selpage;
						}
						for (int i = selpage; i <= reader1.NumberOfPages; i++)
							copy.AddPage(copy.GetImportedPage(reader1, i));
					}
					else if (selpage == 0)  // 最後に追加
					{
						for (int i = 1; i <= reader1.NumberOfPages; i++)
							copy.AddPage(copy.GetImportedPage(reader1, i));
						if (reader2 != null)
						{
							for (int i = 1; i <= reader2.NumberOfPages; i++)
								copy.AddPage(copy.GetImportedPage(reader2, i));
							stat = reader1.NumberOfPages + reader2.NumberOfPages;
						}
						else
						{
							copy.AddPage(copy.GetImportedPage(reader3, 1));
							stat = reader1.NumberOfPages + 1;
						}
					}
				}
			}
			document.Close();
		}
	}
	catch (PdfException ex)
	{
		stat = 0;
		MessageBox.Show(string.Format("ページ挿入に失敗({0})", ex.Message));
	}
	finally
	{
		reader1.Close();
		if (reader2 != null)
			reader2.Close();
		if (reader3 != null)
		{
			reader3.Close();
			uty.FileDelete(emptyFile);
		}
		if (stat != 0)
		{
			uty.FileDelete(file);
			file = temp;
		}
	}
	return stat;
}

-ソフト開発

執筆者:


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

関連記事

VisualStudioのアイコンエディタはクソで、Paint.NETが素晴らしいじゃん

アプリケーションエンジニアならicoファイルを自由自在に作れるかっていうとそうでもなくて、今までだましだまし適当に作っていた。Photoshopなんかで画像を作るとこまではいいんだけど、それをVisu …

no image

デバイスコンテキスト

デバイスコンテキストとは何かを調べてみた。 Device Context=装置の状況。直訳すると意味わからん。状況という言葉があいまいで、背景・場面・状況・文脈という意味があるが、やっぱわからないこと …

VC2017でWindowsの環境変数を取得する方法

C++のUnicodeプロジェクトにおいて、_wgetenvでWindowsの環境変数が取得できるのはわかる。 こんなソースがあって、ビルドしたらエラーになった。 void CMFCApplicati …

no image

Breaker

以前、Layout Breakerという名前のソフトを作ろうと考えていました。レイアウトはともかく、ブレーカー、つまり破壊者です。周囲の反対により、取り下げましたが、個人的にはけっこう気に入っていて、 …

PDFアレコレのユーザーインターフェース改良と回転角度を追加

PDFアレコレのユーザーインターフェース改良と回転角度を追加。 Ver2.07 2020/5/4 ・プレビュー画面のメニューをツールバー形式にして整理した。 ・ページ回転について左90度と180度の回 …