第07回 PDFに日本語を書き出す

今回は書き出すPDFのフォントに日本語を指定します。

shift-jis指定での書き込みもできるのですが、shift-jisを使うのも今更なのでユニコード(ビッグエンディアン)にしました。
文字列をユニコードで書き込むので日本語も何も必要ないのでは?
とも思うのですが日本語限定です。

フォント埋め込みは次回以降です。

■PDFTextWriter.cs
	class PDFTextWriter
	{
		enum FONT_TYPE
		{
			ASCII,
			ADOBE_JPN1_6,
		}


		//クロスリファレンス生成用データ(各オブジェクトの位置を保存)
		List<long> _listnXref = new List<long>();

		//フォントの種類保存用
		IDictionary<string, FONT_TYPE> _mapFontType = new Dictionary<string, FONT_TYPE>();


		public void Close()
		{
			_listnXref.Clear();
			_mapFontType.Clear();
		}


		public bool CreatePDFFile(string strFile)
		{
			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;

					//WriteFont_Ascii(bw, ref nObjIndex, "Times-Italic", "F0");						//欧文フォント指定
					WriteFont_UnicodeJapanese(bw, ref nObjIndex, "KozMinPr6N-Regular", "F0");		//日本語フォント指定(フォント埋め込みなし)
				}

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

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

					//全ページのインデックスを出力
					int nPagesReferenceIndex = nObjIndex;
					{
						listPage.Add(nObjIndex + 1);				//1ページ目のインデックスを渡す
						WritePages(bw, ref nObjIndex, listPage);
					}


					//1ページ出力
					{
						WritePageContentsIndex(bw, ref nObjIndex, nPagesReferenceIndex, nResourceIndex, nObjIndex + 1, 595, 842);

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

							listTexts.Add(new PDFText("F0", 40, 50, 540, @"abcあいうえお漢字123"));			//変更


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

								ms.Flush();

								byte[] data = ms.ToArray();

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

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

			return true;
		}
		/// <summary>
		/// 日本語フォントの指定(フォント埋め込みなし)
		/// 
		/// フォント名は↓
		/// https://www.adobe.com/jp/support/type/aj1-6.html
		/// 小塚明朝 Std Rは「KozMinStd-Regular」
		/// 小塚明朝 Pro Rは「KozMinPro-Regular」
		/// 小塚明朝 Pr6N Rは「KozMinPr6N-Regular」
		/// りょう Text PlusN Rは「RyoTextPlusN-Regular」
		/// </summary>
		void WriteFont_UnicodeJapanese(PDFRawWriter bw, ref int nObjIndex, string strFont, string strFontObjName)
		{
			_listnXref.Add(bw.BaseStream.Position);

			bw.WriteLine("" + nObjIndex + " 0 obj");
			bw.WriteLine("<</Font");
			bw.WriteLine("<</" + strFontObjName);
			bw.WriteLine("<<");
			bw.WriteLine("/Type /Font");
			bw.WriteLine("/BaseFont /" + strFont);
			bw.WriteLine("/Subtype /Type0");
			bw.WriteLine("/Encoding /UniJIS-UTF16-H");			//ユニコード(big endian)
			bw.WriteLine("/DescendantFonts [" + (nObjIndex + 1) + " 0 R]");
			bw.WriteLine(">>");
			bw.WriteLine(">>");
			bw.WriteLine(">>");
			bw.WriteLine("endobj");

			nObjIndex++;

			_listnXref.Add(bw.BaseStream.Position);

			bw.WriteLine("" + nObjIndex + " 0 obj");
			bw.WriteLine("<</Type /Font");
			bw.WriteLine("/Subtype /CIDFontType0");
			bw.WriteLine("/BaseFont /" + strFont);
			bw.WriteLine("/CIDSystemInfo <<");
			bw.WriteLine("/Registry (Adobe)");
			//bw.WriteLine("/Ordering (UCS)");
			bw.WriteLine("/Ordering (Japan1)");
			bw.WriteLine("/Supplement 6");
			bw.WriteLine(">>");
			bw.WriteLine("/FontDescriptor " + (nObjIndex + 1) + " 0 R");
			bw.WriteLine(">>");
			bw.WriteLine("endobj");

			nObjIndex++;

			_listnXref.Add(bw.BaseStream.Position);

			bw.WriteLine("" + nObjIndex + " 0 obj");
			bw.WriteLine("<<");
			bw.WriteLine("/Type /FontDescriptor");
			bw.WriteLine("/FontName /" + strFont);
			bw.WriteLine("/Flags 4");
			//bw.WriteLine("/FontBBox [-437 -340 1147 1317]");
			bw.WriteLine("/FontBBox [0 0 0 0]");			//↓この辺どう取得したらいいのか不明
			bw.WriteLine("/ItalicAngle 0");
			bw.WriteLine("/Ascent 1317");
			bw.WriteLine("/Descent -349");
			bw.WriteLine("/CapHeight 742");
			bw.WriteLine("/StemV 80");
			bw.WriteLine(">>");
			bw.WriteLine("endobj");

			nObjIndex++;


			//フォント情報の保存
			_mapFontType.Add(strFontObjName, FONT_TYPE.ADOBE_JPN1_6);
		}


		void WritePageContentsIndex(PDFRawWriter bw, ref int nObjIndex, int nParentObjIndex, int nResourcesObjIndex, int nContentsObjIndex, float fWidth, float fHeight)
		{
			_listnXref.Add(bw.BaseStream.Position);
			{
				bw.WriteLine("" + nObjIndex + " 0 obj");
				bw.WriteLine("<</Type /Page");
				bw.WriteLine("/Parent " + nParentObjIndex + " 0 R");
				bw.WriteLine("/Resources " + nResourcesObjIndex + " 0 R");
				bw.WriteLine("/MediaBox [0 0 " + fWidth + " " + fHeight + "]");
				bw.WriteLine("/Contents " + nContentsObjIndex + " 0 R");
				bw.WriteLine(">>");
				bw.WriteLine("endobj");
			}
			nObjIndex++;
		}


		/// <summary>
		/// 与えられたテキストデータをストリームに以下の形式で書き込む
		/// "BT /F0 10 Tf 150 200 Td (hello) Tj ET\r";
		/// </summary>
		void PrepareTextContents(Stream strm, List<PDFText> listTexts)
		{
			using (MemoryStream ms = new MemoryStream())
			using (BinaryWriter bwms = new BinaryWriter(ms))
			{
				foreach (PDFText text in listTexts)
				{
					bool ret;
					FONT_TYPE type;

					ret = _mapFontType.TryGetValue(text.strFont, out type);
					if (ret == false)
					{
						//フォントの種類特定失敗
						Debug.Assert(false);
						continue;
					}


					string strText = text.strText;

					byte[] raw2 = null;

					{
						byte[] tmp = null;

						//フォントに応じてエンコーディング
						if (type == FONT_TYPE.ASCII)
							tmp = Encoding.ASCII.GetBytes(strText);
						if (type == FONT_TYPE.ADOBE_JPN1_6)
							tmp = Encoding.BigEndianUnicode.GetBytes(strText);
						else
						{
							Debug.Assert(false);
						}

						// 「()\」をエスケープする
						using (MemoryStream ms_esc = new MemoryStream())
						{
							foreach (byte cb in tmp)
							{
								if (cb == 0x28		//'('
									|| cb == 0x29	//')'
									|| cb == 0x5c)	//'\'
								{
									ms_esc.WriteByte(0x5c);		//'\'
								}
								ms_esc.WriteByte(cb);
							}
							ms_esc.Flush();

							raw2 = ms_esc.ToArray();
						}
					}

					string strData = "BT /" + text.strFont + " " + text.fPoint + " Tf " + text.fX + " " + text.fY + " Td (";
					byte[] raw1 = Encoding.ASCII.GetBytes(strData);
					byte[] raw3 = Encoding.ASCII.GetBytes(") Tj ET");

					bwms.Write(raw1);
					bwms.Write(raw2);
					bwms.Write(raw3);
					bwms.Write('\r');
				}
				ms.Flush();

				byte[] data = ms.ToArray();
				strm.Write(data, 0, data.Length);
			}
		}

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


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