今回は書き出すPDFのフォントに日本語を指定します。
shift-jis指定での書き込みもできるのですが、shift-jisを使うのも今更なのでユニコード(ビッグエンディアン)にしました。
文字列をユニコードで書き込むので日本語も何も必要ないのでは?
とも思うのですが日本語限定です。
フォント埋め込みは次回以降です。
■PDFTextWriter.csclass 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); } }