第16回 フォント名をフォントファイルから取得する

今回はopen type fontのフォントファイルからフォント名/フォントファミリー名を取得します。

open typeのファイル仕様に沿ってファイルを読むのみです。
フォント名はプラットフォームや表示言語に応じて複数格納されています。
今回はWindowsのユニコード向け、英語圏用の名前を取得/利用しています。

■PDFTextWrite.cs
		/// <summary>
		/// フォントの埋め込み
		/// 
		/// フォント名/フォントファミリー名を指定しない場合はフォントファイルから自動取得を試みる
		/// </summary>
		void WriteFont_EmbeddedUnicode(PDFRawWriter bw, ref int nObjIndex, string strFontObjName, string strFontFile, string strFont = "", string strFontFamily = "")
		{
			ushort nRangeMin = 0xFFFF;
			ushort nRangeMax = 0;

			//opentypeフォントファイルからcmapを読み込む
			IDictionary<ushort, byte[]> cmap = LoadCMap(strFontFile, out nRangeMin, out nRangeMax, ref strFontFamily, ref strFont);
			_cmapFonts.Add(strFontObjName, cmap);


			_listnXref.Add(bw.BaseStream.Position);
			{
				bw.WriteLine("" + nObjIndex + " 0 obj");
				bw.WriteLine("<</Type /Font");
				bw.WriteLine("/BaseFont /" + strFont);
				bw.WriteLine("/Subtype /Type0");
				bw.WriteLine("/Encoding /Identity-H");			//PDF独自のエンコード
				bw.WriteLine("/DescendantFonts [" + (nObjIndex + 1) + " 0 R]");
				bw.WriteLine("/ToUnicode " + (nObjIndex + 4) + " 0 R");		//ToUnicode変換表
				bw.WriteLine(">>");
				bw.WriteLine("endobj");
			}
			nObjIndex++;


			int nDescendantFontsObjIndex = 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("/CIDToGIDMap/Identity");
				bw.WriteLine("/CIDSystemInfo <<");
				bw.WriteLine("/Registry (Adobe)");
				bw.WriteLine("/Ordering (Identity)");		//Japan1にはしない
				bw.WriteLine("/Supplement 0");				//6にした方がいい?
				bw.WriteLine(">>");
				bw.WriteLine("/FontDescriptor " + (nObjIndex + 1) + " 0 R");
				bw.WriteLine(">>");
				bw.WriteLine("endobj");
			}
			nObjIndex++;





			////CMAPの準備
			////{
			//	string strFontCMapFile = @"cmap_msgothic.txt";

			//	//
			//	//CMAPの読み込み
			//	//
			//	//以下を実行してcmap.txtを取得、その中から「Char 30D6 -> Index 2121」というようなunicode用のcmapテーブルを抜き出してcmap_msgothic.txtに保存
			//	// ttfdump.exe HuiFont29.ttf -tcmap -nx >cmap.txt

		/// <summary>
		/// open type fontファイルからcmapを読み取る
		/// 
		/// open type fontの仕様通りにファイルを読むだけ
		/// マジックナンバーなどでファイルチェックをするべきだがしていない
		///
		/// strFontFamilyName/strFontPostScriptName は==""だった場合のみ、フォントファイルから読み出す
		/// 
		/// 仕様
		/// https://www.microsoft.com/typography/otspec/otff.htm
		/// </summary>
		IDictionary<ushort, byte[]> LoadCMap(string strFontFile, out ushort nRangeMin, out ushort nRangeMax, ref string strFontFamilyName, ref string strFontPostScriptName)//, out int nBBXMin, out int nBBXMax, out int nBBYMin, out int nBBYMax, out int nAscender, out int nDescender)
		{
			IDictionary<ushort, byte[]> cmap = new Dictionary<ushort, byte[]>();

			nRangeMin = 0xFFFF;
			nRangeMax = 0;

			int nBBXMin = 0;
			int nBBXMax = 0;
			int nBBYMin = 0;
			int nBBYMax = 0;

			int nAscender = 0;
			int nDescender = 0;

			using (FileStream fs = new FileStream(strFontFile, FileMode.Open, FileAccess.Read))
			using (BinaryReader br = new BinaryReader(fs))
			{
				// https://www.microsoft.com/typography/otspec/otff.htm
				byte[] sfntVer = br.ReadBytes(4);
				uint nTableCount = ByteToUInt_BE(br.ReadBytes(2));
				uint nSearchRange = ByteToUInt_BE(br.ReadBytes(2));
				uint nEntrySelector = ByteToUInt_BE(br.ReadBytes(2));
				uint nRangeShift = ByteToUInt_BE(br.ReadBytes(2));

				uint nCMapOffset = 0;
				uint nCMapLength = 0;

				uint nHeadOffset = 0;
				uint nHeadLength = 0;

				uint nHheaOffset = 0;
				uint nHheaLength = 0;

				uint nOS2Offset = 0;
				uint nOS2Length = 0;

				uint nNameOffset = 0;
				uint nNameLength = 0;

				for (uint i = 0; i < nTableCount; i++)
				{
					byte[] tag = br.ReadBytes(4);
					uint checkSum = ByteToUInt_BE(br.ReadBytes(4));
					uint offset = ByteToUInt_BE(br.ReadBytes(4));		//	Offset from beginning of TrueType font file.
					uint length = ByteToUInt_BE(br.ReadBytes(4));

					string strTag = Encoding.ASCII.GetString(tag);
					if (strTag == "cmap")
					{
						nCMapOffset = offset;
						nCMapLength = length;
					}
					if (strTag == "head")
					{
						nHeadOffset = offset;
						nHeadLength = length;
					}
					if (strTag == "hhea")
					{
						nHheaOffset = offset;
						nHheaLength = length;
					}
					if (strTag == "OS/2")
					{
						nOS2Offset = offset;
						nOS2Length = length;
					}
					if (strTag == "name")
					{
						nNameOffset = offset;
						nNameLength = length;
					}
				}


				if (strFontFamilyName == "" || strFontPostScriptName == "")
				{
					if (nNameOffset > 0 && nNameLength > 0)
					{
						fs.Seek(nNameOffset, SeekOrigin.Begin);

						// https://www.microsoft.com/typography/otspec/name.htm
						uint format = ByteToUInt_BE(br.ReadBytes(2));
						uint count = ByteToUInt_BE(br.ReadBytes(2));			//Number of name records.
						uint stringOffset = ByteToUInt_BE(br.ReadBytes(2));		//Offset to start of string storage (from start of table).

						//プラットフォーム3、エンコーディング1(Windowsのunicode)で、さらにランゲージID1033(英語圏用の名前)のデータのみ収集
						List<uint> list31NameID = new List<uint>();
						List<uint> list31Length = new List<uint>();
						List<uint> list31Offset = new List<uint>();

						//Name Records
						for (uint i = 0; i < count; i++)
						{
							uint platformID = ByteToUInt_BE(br.ReadBytes(2));
							uint encodingID = ByteToUInt_BE(br.ReadBytes(2));
							uint languageID = ByteToUInt_BE(br.ReadBytes(2));
							uint nameID = ByteToUInt_BE(br.ReadBytes(2));
							uint length = ByteToUInt_BE(br.ReadBytes(2));		//String length (in bytes).
							uint offset = ByteToUInt_BE(br.ReadBytes(2));		//String offset from start of storage area (in bytes).

							//プラットフォーム3、エンコーディング1(Windowsのunicode)で、さらにランゲージID1033(英語圏用の名前)のデータのみ収集
							if (platformID != 3 || encodingID != 1 || languageID != 1033)
								continue;

							list31NameID.Add(nameID);
							list31Length.Add(length);
							list31Offset.Add(offset);
						}

						//format==1の場合のみ言語タグ情報がある
						if (format == 1)
						{
							uint langTagCount = ByteToUInt_BE(br.ReadBytes(2));

							//LangTagRecord
							for (uint i = 0; i < langTagCount; i++)
							{
								uint length = ByteToUInt_BE(br.ReadBytes(2));
								uint offset = ByteToUInt_BE(br.ReadBytes(2));		//	Language-tag string offset from start of storage area (in bytes).
							}
						}

						//Storage area開始
						long nStorageStart = fs.Position;

						for (int i = 0; i < list31NameID.Count; i++)
						{
							if (list31NameID[i] == 1)		//font family name
							{
								if (strFontFamilyName == "")
								{
									fs.Seek(nStorageStart + list31Offset[i], SeekOrigin.Begin);
									strFontFamilyName = Encoding.BigEndianUnicode.GetString(br.ReadBytes((int)list31Length[i]));
								}
							}

							if (list31NameID[i] == 6)		//Postscript name for the font
							{
								if (strFontPostScriptName == "")
								{
									fs.Seek(nStorageStart + list31Offset[i], SeekOrigin.Begin);
									strFontPostScriptName = Encoding.BigEndianUnicode.GetString(br.ReadBytes((int)list31Length[i]));
								}
							}
						}
					}
				}

				if (nHheaOffset > 0 && nHheaLength > 0)
				{
					fs.Seek(nHheaOffset, SeekOrigin.Begin);

					// https://www.microsoft.com/typography/otspec/hhea.htm
					byte[] version = br.ReadBytes(4);
					nAscender = ByteToInt_BE(br.ReadBytes(2));
					nDescender = ByteToInt_BE(br.ReadBytes(2));
					int LineGap = ByteToInt_BE(br.ReadBytes(2));
					uint advanceWidthMax = ByteToUInt_BE(br.ReadBytes(2));
					int minLeftSideBearing = ByteToInt_BE(br.ReadBytes(2));
					int minRightSideBearing = ByteToInt_BE(br.ReadBytes(2));
					int xMaxExtent = ByteToInt_BE(br.ReadBytes(2));
					int caretSlopeRise = ByteToInt_BE(br.ReadBytes(2));
					int caretSlopeRun = ByteToInt_BE(br.ReadBytes(2));
					int caretOffset = ByteToInt_BE(br.ReadBytes(2));
					int reserved1 = ByteToInt_BE(br.ReadBytes(2));
					int reserved2 = ByteToInt_BE(br.ReadBytes(2));
					int reserved3 = ByteToInt_BE(br.ReadBytes(2));
					int reserved4 = ByteToInt_BE(br.ReadBytes(2));
					int metricDataFormat = ByteToInt_BE(br.ReadBytes(2));
					uint numberOfHMetrics = ByteToUInt_BE(br.ReadBytes(2));
				}
		/// <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, @"msgothic.otf");//, "MS-Gothic", "MS Gothic");
						}

						//フォント一覧のみのリソース
						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++;
					}
				}
		/// <summary>
		/// PDF作成例
		/// </summary>
		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;



					//フォント埋め込み
					{
						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, @"msgothic.otf", "MS-Gothic", "MS Gothic");
						}
						{
							string strFontObjName = "F1";
							listFonts.Add(new KeyValuePair<string, int>(strFontObjName, nObjIndex));
							WriteFont_Ascii(bw, ref nObjIndex, "Times-Italic", strFontObjName);						//欧文フォント指定
						}
						{
							string strFontObjName = "F2";
							listFonts.Add(new KeyValuePair<string, int>(strFontObjName, nObjIndex));
							WriteFont_UnicodeJapanese(bw, ref nObjIndex, "KozMinPr6N-Regular", strFontObjName);		//日本語フォント指定(フォント埋め込みなし)
						}

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


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