第13回 小説家になろう縦書きPDFを変換する

今回はようやく目的の機能実装です。

「小説家になろう」からダウンロードした縦書きPDFファイルを読み込む処理は以前に作成しました。
そこに含まれるテキスト情報を横書きPDFとして変換しながら書き出します。
PDFの書き台処理もこれまでに作成したものがそのまま利用できるため、体裁を整える作業のみです。

DSC_1343.JPG
「小説家になろう」からダウンロードした縦書きPDFに対してAcrobatでフォントを埋め込んで、Kindleに転送/表示した状態がこれ。

文字が小さく表示が薄いなど読みにくい状態。

フォントの埋め込み処理もかなり時間がかかります。


DSC_1342.JPG
今回作成した処理で縦書きPDFから横書きPDFへ変換した状態がこれ。

かなり改善して読みやすくなりました。変換速度も気になるほど遅くありません。

難はルビ位置。ルビが正しい位置に表示されません。

■Form1.cs
		public Form1()
		{
			InitializeComponent();

			PDFTextReader pr = new PDFTextReader();
			bool ret = pr.Read(@"c:\N9442CW.pdf");

			PDFTextWriter pw = new PDFTextWriter();
			pw.ConvertPDFFile("test.pdf", pr);

			PictureBox pictureBox1 = new PictureBox();
			pictureBox1.Parent = this;
			pictureBox1.Dock = DockStyle.Fill;
■PDFTextWriter.cs
		/// <summary>
		/// "小説家になろう"の縦書きPDF変換処理
		/// </summary>
		public bool ConvertPDFFile(string strFile, PDFTextReader srcPDF)
		{
			Close();

			int nObjIndex = 1;

			using (FileStream fs = new FileStream(strFile, FileMode.Create, FileAccess.Write))
			using (PDFRawWriter bw = new PDFRawWriter(fs))
			{
				//ヘッダー出力
				bw.WriteLine("%PDF-1.7");

				//フォント出力
				int nResourceIndex;
				{
					nResourceIndex = nObjIndex;



					//フォント埋め込み
					{
						List<KeyValuePair<string, int>> listFonts = new List<KeyValuePair<string, int>>();

						{
							string strFontObjName = "F0";
							listFonts.Add(new KeyValuePair<string, int>(strFontObjName, nObjIndex));
							WriteFont_EmbeddedUnicode(bw, ref nObjIndex, strFontObjName, "MS-Gothic", "MS Gothic", @"msgothic.otf");
						}

						//フォント一覧のみのリソース
						nResourceIndex = nObjIndex;
						_listnXref.Add(bw.BaseStream.Position);
						{
							bw.WriteLine("" + nObjIndex + " 0 obj");
							bw.WriteLine("<</Font");
							bw.WriteLine("<<");
							foreach (KeyValuePair<string, int> pair in listFonts)
							{
								bw.WriteLine("/" + pair.Key + " " + pair.Value + " 0 R");
							}
							bw.WriteLine(">>");
							bw.WriteLine(">>");
							bw.WriteLine("endobj");
						}
						nObjIndex++;
					}
				}

				//カタログ出力
				int nRoot = nObjIndex;
				{
					WriteCatalog(bw, ref nObjIndex, nObjIndex + 1);
				}

				//ページ出力
				{
					List<int> listPage = new List<int>();

					//全ページのインデックスを出力
					int nPagesReferenceIndex = nObjIndex;
					{
						for (int i = 0; i < srcPDF.Pages.Count; i++)
						{
							listPage.Add(nObjIndex + 1 + i * 2);	//iページ目のインデックスを渡す
						}

						WritePages(bw, ref nObjIndex, listPage);
					}


					//サイズは適当に決定
					int nWidth = 455;
					int nHeight = 615;

					//ページ出力
					for (int i = 0; i < srcPDF.Pages.Count; i++)
					{
						PDFPage page = srcPDF.Pages[i];

						WritePageContentsIndex(bw, ref nObjIndex, nPagesReferenceIndex, nResourceIndex, nObjIndex + 1, nWidth, nHeight);

						//ページテキスト出力
						{
							List<PDFText> listTexts = new List<PDFText>();

							//テキストの準備
							{
								float y = nHeight - 5;
								foreach (object item in page.Items)
								{
									if (item.GetType() != typeof(PDFText))
										continue;

									PDFText text = (PDFText)item;

									//ページ番号は左下に表示
									if ((int)(text.fY) == 56)		//"小説家になろう"PDFのページ番号y座標は56.7?
									{
										listTexts.Add(new PDFText("F0", text.fPoint, 5, 5, text.strText));
										continue;
									}

									//表紙(i == 0)と最終ページ以外はオリジナルの行間隔で表示
									if (i > 0 && i < srcPDF.Pages.Count - 1)
										y = (int)(text.fX * nHeight / 842.0);
									else
										y -= 17;		//表紙(i == 0)と最終ページは固定行間隔で表示

									listTexts.Add(new PDFText("F0", text.fPoint, 10, y, text.strText));
								}
							}




							//コンテンツの書き出し
							using (MemoryStream ms = new MemoryStream())
							using (BinaryWriter bwms = new BinaryWriter(ms))
							{
								//文字データをPDF出力用に準備
								PrepareTextContents(ms, listTexts);

								//Kindleはコンテンツ内容によって勝手にズーム表示しちゃうから、
								//すべてのページに枠線を描くことで勝手にズームしないようにする
								//座標固定で枠線を描く
								PrepareLineContents(ms, 2, 2, 2, nHeight - 2);
								PrepareLineContents(ms, 2, 2, nWidth - 2, 2);
								PrepareLineContents(ms, nWidth - 2, 2, nWidth - 2, nHeight - 2);
								PrepareLineContents(ms, 2, nHeight - 2, nWidth - 2, nHeight - 2);

								ms.Flush();

								byte[] data = ms.ToArray();

								//ページコンテンツの出力
								WriteFlateData(bw, ref nObjIndex, data);
							}
						}
					}
				}

				//クロスリファレンス/トレーラー出力
				WriteXrefTrailer(bw, nRoot);
			}

			return true;
		}


		/// <summary>
		/// ストリームに線データを書き込む
		/// </summary>
		void PrepareLineContents(Stream strm, int x1, int y1, int x2, int y2)
		{
			byte[] data = Encoding.ASCII.GetBytes(string.Format("{0} {1} m {2} {3} l S\r", x1, y1, x2, y2));
			strm.Write(data, 0, data.Length);
		}

プロジェクトファイルをダウンロード


カテゴリー「PDFを処理する(C#)」 のエントリー