none
PDFsharpで日本語が表示できない RRS feed

  • 質問

  • お世話になります。黒と申します。

    VS2015pro 使用言語はvisualbasic フォームアプリケーションでアプリを作っています。 

    PDFsharpでデスクトップアプリからダイレクトにPDFファイルをつくるアプリを作っています。

    日本語をPDFに表示させたいのですが、MSgothicを呼べないとエラーが表示されてしまい困っています。

    エラー内容:オープンタイプのフォントの解析中にエラーが出ました。

    MSゴシックを使用したいんですが…。

    アドバイスをお願いいたします。

    2023年3月13日 6:16

回答

  • 色々いじくってみましたがPDFSharpだとフォントがまともに扱えなさそうなので、PDFプリンタであるMicrosoft Print To PDFで印刷して、ファイルに出力をさせた方が楽かもしれない

    Imports System
    Imports System.Collections.Generic
    Imports System.Linq
    Imports System.Drawing.Imaging
    Imports System.Drawing.Printing
    Imports System.IO
    Imports Microsoft.Ink
    
    Public Class Form1
    
        Sub New()
            InitializeComponent()
        End Sub
    
        Private Sub PrintButton1_Click(sender As Object, e As EventArgs) Handles PrintButton1.Click
    
            Dim pdfPrinterName As String = PrinterSettings.InstalledPrinters.OfType(Of String)().FirstOrDefault(Function(s) s.Equals("Microsoft Print to PDF", StringComparison.OrdinalIgnoreCase))
            If String.IsNullOrWhiteSpace(pdfPrinterName) Then
                MessageBox.Show("Microsoft Print to PDFが見つかりませんでした")
                Exit Sub
            End If
    
            Dim doc As New PrintDocument()
            doc.PrintController = New System.Drawing.Printing.StandardPrintController()
            doc.PrinterSettings.PrinterName = pdfPrinterName
            doc.PrinterSettings.PrintToFile = True 'ファイルに出力するように
            doc.PrinterSettings.PrintFileName = DateTime.Now.ToString("yyyyMMddHHmmss") & ".pdf"
    
            AddHandler doc.PrintPage, AddressOf OnPrintPage
    
            doc.Print()
        End Sub
    
        ''' <summary>ページに印刷する内容を描画</summary>
        Private Sub OnPrintPage(s As Object, e As PrintPageEventArgs)
            Dim offsetX As Integer = 10
            Dim offsetY As Integer = 10
    
            For Each tabPage As TabPage In Me.TabControl1.TabPages
                If tabPage.Width = 0 OrElse tabPage.Height = 0 Then
                    Continue For
                End If
    
                e.Graphics.ResetClip()
                e.Graphics.DrawRectangle(Pens.Black, offsetX, offsetY, tabPage.Width, tabPage.Height)
    
                For Each pbox As PictureBox In tabPage.Controls.OfType(Of PictureBox)
                    If pbox.GetType() = GetType(PictureBox) Then
                        PrintImage(e, offsetX, offsetY, tabPage, pbox)
                    ElseIf TypeOf pbox Is InkPicture Then
                        'InkPictureはPictureBoxを継承している
                        PrintImage(e, offsetX, offsetY, tabPage, pbox)
                    Else
                        'PictureBoxを継承している何か
                    End If
    
                Next
    
                e.Graphics.ResetClip()
    
                Dim lcid = System.Globalization.CultureInfo.GetCultureInfo("ja-jp").LCID
                Dim ff As FontFamily = FontFamily.Families.FirstOrDefault(Function(f) f.GetName(lcid) = "MS ゴシック")
                Dim font As Font = New Font(ff, 20, FontStyle.Regular)
                e.Graphics.DrawString("ページ" + tabPage.Text, font, Brushes.Red, New PointF(tabPage.Width + 10, offsetY))
    
                offsetY += tabPage.Height
                If offsetY >= e.PageBounds.Height Then
                    Exit For
                End If
            Next
            e.HasMorePages = False
        End Sub
    
        Private Sub PrintImage(e As PrintPageEventArgs, offsetX As Double, offsetY As Double, tabPage As TabPage, pbox As PictureBox)
            Dim img As Image = pbox.Image
    
            If img IsNot Nothing Then
                '画像が指定してあるなら画像を印刷
                PrintImage(e, tabPage, offsetX, offsetY, pbox, pbox.Image, pbox.SizeMode)
            End If
    
            If TypeOf pbox Is InkPicture Then
                'InkPictureの場合
    
                Dim ip As InkPicture = CType(pbox, InkPicture)
                If ip.Ink.Strokes.Count > 0 Then
    
                    'PictureBoxのImageの上に重ね描きされるストロークを画像に
                    Dim gifData As Byte() = ip.Ink.Save(PersistenceFormat.Gif, CompressionMode.NoCompression)
                    Dim ms As New System.IO.MemoryStream(gifData)
                    Dim gif = System.Drawing.Image.FromStream(ms)
    
                    'PictureBoxの大きさのBitmapにストロークを描画しなおし
                    Dim bmp = New Bitmap(pbox.Width, pbox.Height, PixelFormat.Format32bppArgb)
                    Using g As Graphics = Graphics.FromImage(bmp)
                        Dim bound = ip.Ink.GetBoundingBox()
                        g.DrawImage(gif, New PointF(bound.X / 25.4, bound.Y / 25.4))
                    End Using
    
                    'ストローク画像を印刷させる
                    PrintImage(e, tabPage, offsetX, offsetY, pbox, bmp, PictureBoxSizeMode.Normal)
                End If
            End If
        End Sub
    
        Private Sub PrintImage(e As PrintPageEventArgs, tabPage As TabPage, offsetX As Double, offsetY As Double, pbox As PictureBox, img As Image, sizeMode As PictureBoxSizeMode)
            If pbox.Width = 0 OrElse pbox.Height = 0 OrElse img Is Nothing OrElse img.Width = 0 OrElse img.Height = 0 Then
                Exit Sub
            End If
    
            Dim rectClip As Rectangle '用紙上の描画可能な範囲
            Dim rectPrint As Rectangle 'Imageを描画する範囲
            Dim rectImage As Rectangle = New Rectangle(0, 0, img.Width, img.Height) 'Imageから切り出す範囲
    
            rectPrint = New Rectangle(pbox.Left, pbox.Top, pbox.Width, pbox.Height)
            rectClip = New Rectangle(0, 0, tabPage.Width, tabPage.Height)
            rectClip.Intersect(rectPrint)
    
            'PictureBoxのモードによって異なる表示され方を変換
            Select Case sizeMode
                Case PictureBoxSizeMode.AutoSize
                    rectPrint = New Rectangle(pbox.Left, pbox.Top, img.Width, img.Height)
    
                Case PictureBoxSizeMode.Normal
                    rectPrint = New Rectangle(pbox.Left, pbox.Top, img.Width, img.Height)
    
                Case PictureBoxSizeMode.StretchImage
                    rectPrint = New Rectangle(pbox.Left, pbox.Top, pbox.Width, pbox.Height)
    
                Case PictureBoxSizeMode.CenterImage
                    rectPrint = New Rectangle(pbox.Left + pbox.Width / 2 - img.Width / 2, pbox.Top + pbox.Height / 2 - img.Height / 2, img.Width, img.Height)
    
                Case PictureBoxSizeMode.Zoom
                    Dim centerX As Single = pbox.Left + pbox.Width / 2
                    Dim centerY As Single = pbox.Top + pbox.Height / 2
                    Dim scale As Single
                    If (img.Height / img.Width) < (pbox.Height / pbox.Width) Then
                        scale = pbox.Width / img.Width
                    Else
                        scale = pbox.Height / img.Height
                    End If
                    rectPrint = New Rectangle(centerX - img.Width * scale, centerY - img.Height * scale, img.Width * scale, img.Height * scale)
            End Select
    
            rectClip.Offset(offsetX, offsetY)
            rectPrint.Offset(offsetX, offsetY)
    
            e.Graphics.SetClip(rectClip)
            e.Graphics.DrawImage(img, rectPrint, rectImage, GraphicsUnit.Pixel)
        End Sub
    
    End Class
    

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク 9638masa 2023年4月1日 4:51
    2023年3月16日 12:21

すべての返信

  • いろいろ試していたら游明朝は使えました。

    游ゴシックも使えるという記事を見ましたが、使えませんでした。

    なぜ駄目なんだろう?

    源真ゴシックもc#で使っている方はいるみたいですが、自分が未熟すぎてわかりませんでした。

    2023年3月14日 1:36
  • OS組み込みのフォントは、OSのバージョンアップなどに伴って細かくバージョンアップしていたりします。

    フォントのバージョンアップで追加された機能が、PdfSharpが扱えない機能を含んでいたという可能性もあります。

    PdfSharpは最終更新がだいぶ前のライブラリなので、フォントの新しい機能に対応していないのかもしません。

    // あくまで可能性の話であって、実際はどうだかわかりませんが。

    使えるのであれば、PdfSharpからフォークしたPdfSharpCoreなどに乗り換えたほうがいいような気がします。

    2023年3月14日 9:22
  • Hongliangさま

    アドバイスありがとうございます。

    PDFsharpもなんか色々出ているみたいですね。いろいろ試してみます。

    2023年3月15日 5:09
  • 色々いじくってみましたがPDFSharpだとフォントがまともに扱えなさそうなので、PDFプリンタであるMicrosoft Print To PDFで印刷して、ファイルに出力をさせた方が楽かもしれない

    Imports System
    Imports System.Collections.Generic
    Imports System.Linq
    Imports System.Drawing.Imaging
    Imports System.Drawing.Printing
    Imports System.IO
    Imports Microsoft.Ink
    
    Public Class Form1
    
        Sub New()
            InitializeComponent()
        End Sub
    
        Private Sub PrintButton1_Click(sender As Object, e As EventArgs) Handles PrintButton1.Click
    
            Dim pdfPrinterName As String = PrinterSettings.InstalledPrinters.OfType(Of String)().FirstOrDefault(Function(s) s.Equals("Microsoft Print to PDF", StringComparison.OrdinalIgnoreCase))
            If String.IsNullOrWhiteSpace(pdfPrinterName) Then
                MessageBox.Show("Microsoft Print to PDFが見つかりませんでした")
                Exit Sub
            End If
    
            Dim doc As New PrintDocument()
            doc.PrintController = New System.Drawing.Printing.StandardPrintController()
            doc.PrinterSettings.PrinterName = pdfPrinterName
            doc.PrinterSettings.PrintToFile = True 'ファイルに出力するように
            doc.PrinterSettings.PrintFileName = DateTime.Now.ToString("yyyyMMddHHmmss") & ".pdf"
    
            AddHandler doc.PrintPage, AddressOf OnPrintPage
    
            doc.Print()
        End Sub
    
        ''' <summary>ページに印刷する内容を描画</summary>
        Private Sub OnPrintPage(s As Object, e As PrintPageEventArgs)
            Dim offsetX As Integer = 10
            Dim offsetY As Integer = 10
    
            For Each tabPage As TabPage In Me.TabControl1.TabPages
                If tabPage.Width = 0 OrElse tabPage.Height = 0 Then
                    Continue For
                End If
    
                e.Graphics.ResetClip()
                e.Graphics.DrawRectangle(Pens.Black, offsetX, offsetY, tabPage.Width, tabPage.Height)
    
                For Each pbox As PictureBox In tabPage.Controls.OfType(Of PictureBox)
                    If pbox.GetType() = GetType(PictureBox) Then
                        PrintImage(e, offsetX, offsetY, tabPage, pbox)
                    ElseIf TypeOf pbox Is InkPicture Then
                        'InkPictureはPictureBoxを継承している
                        PrintImage(e, offsetX, offsetY, tabPage, pbox)
                    Else
                        'PictureBoxを継承している何か
                    End If
    
                Next
    
                e.Graphics.ResetClip()
    
                Dim lcid = System.Globalization.CultureInfo.GetCultureInfo("ja-jp").LCID
                Dim ff As FontFamily = FontFamily.Families.FirstOrDefault(Function(f) f.GetName(lcid) = "MS ゴシック")
                Dim font As Font = New Font(ff, 20, FontStyle.Regular)
                e.Graphics.DrawString("ページ" + tabPage.Text, font, Brushes.Red, New PointF(tabPage.Width + 10, offsetY))
    
                offsetY += tabPage.Height
                If offsetY >= e.PageBounds.Height Then
                    Exit For
                End If
            Next
            e.HasMorePages = False
        End Sub
    
        Private Sub PrintImage(e As PrintPageEventArgs, offsetX As Double, offsetY As Double, tabPage As TabPage, pbox As PictureBox)
            Dim img As Image = pbox.Image
    
            If img IsNot Nothing Then
                '画像が指定してあるなら画像を印刷
                PrintImage(e, tabPage, offsetX, offsetY, pbox, pbox.Image, pbox.SizeMode)
            End If
    
            If TypeOf pbox Is InkPicture Then
                'InkPictureの場合
    
                Dim ip As InkPicture = CType(pbox, InkPicture)
                If ip.Ink.Strokes.Count > 0 Then
    
                    'PictureBoxのImageの上に重ね描きされるストロークを画像に
                    Dim gifData As Byte() = ip.Ink.Save(PersistenceFormat.Gif, CompressionMode.NoCompression)
                    Dim ms As New System.IO.MemoryStream(gifData)
                    Dim gif = System.Drawing.Image.FromStream(ms)
    
                    'PictureBoxの大きさのBitmapにストロークを描画しなおし
                    Dim bmp = New Bitmap(pbox.Width, pbox.Height, PixelFormat.Format32bppArgb)
                    Using g As Graphics = Graphics.FromImage(bmp)
                        Dim bound = ip.Ink.GetBoundingBox()
                        g.DrawImage(gif, New PointF(bound.X / 25.4, bound.Y / 25.4))
                    End Using
    
                    'ストローク画像を印刷させる
                    PrintImage(e, tabPage, offsetX, offsetY, pbox, bmp, PictureBoxSizeMode.Normal)
                End If
            End If
        End Sub
    
        Private Sub PrintImage(e As PrintPageEventArgs, tabPage As TabPage, offsetX As Double, offsetY As Double, pbox As PictureBox, img As Image, sizeMode As PictureBoxSizeMode)
            If pbox.Width = 0 OrElse pbox.Height = 0 OrElse img Is Nothing OrElse img.Width = 0 OrElse img.Height = 0 Then
                Exit Sub
            End If
    
            Dim rectClip As Rectangle '用紙上の描画可能な範囲
            Dim rectPrint As Rectangle 'Imageを描画する範囲
            Dim rectImage As Rectangle = New Rectangle(0, 0, img.Width, img.Height) 'Imageから切り出す範囲
    
            rectPrint = New Rectangle(pbox.Left, pbox.Top, pbox.Width, pbox.Height)
            rectClip = New Rectangle(0, 0, tabPage.Width, tabPage.Height)
            rectClip.Intersect(rectPrint)
    
            'PictureBoxのモードによって異なる表示され方を変換
            Select Case sizeMode
                Case PictureBoxSizeMode.AutoSize
                    rectPrint = New Rectangle(pbox.Left, pbox.Top, img.Width, img.Height)
    
                Case PictureBoxSizeMode.Normal
                    rectPrint = New Rectangle(pbox.Left, pbox.Top, img.Width, img.Height)
    
                Case PictureBoxSizeMode.StretchImage
                    rectPrint = New Rectangle(pbox.Left, pbox.Top, pbox.Width, pbox.Height)
    
                Case PictureBoxSizeMode.CenterImage
                    rectPrint = New Rectangle(pbox.Left + pbox.Width / 2 - img.Width / 2, pbox.Top + pbox.Height / 2 - img.Height / 2, img.Width, img.Height)
    
                Case PictureBoxSizeMode.Zoom
                    Dim centerX As Single = pbox.Left + pbox.Width / 2
                    Dim centerY As Single = pbox.Top + pbox.Height / 2
                    Dim scale As Single
                    If (img.Height / img.Width) < (pbox.Height / pbox.Width) Then
                        scale = pbox.Width / img.Width
                    Else
                        scale = pbox.Height / img.Height
                    End If
                    rectPrint = New Rectangle(centerX - img.Width * scale, centerY - img.Height * scale, img.Width * scale, img.Height * scale)
            End Select
    
            rectClip.Offset(offsetX, offsetY)
            rectPrint.Offset(offsetX, offsetY)
    
            e.Graphics.SetClip(rectClip)
            e.Graphics.DrawImage(img, rectPrint, rectImage, GraphicsUnit.Pixel)
        End Sub
    
    End Class
    

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク 9638masa 2023年4月1日 4:51
    2023年3月16日 12:21
  • gekka様

    アドバイスいつもありがとうございます。

    Inkpictureについてもアドバイスいただけて本当に助かります。

    後で試してみます!

    2023年3月17日 8:24
  • gekka様

    アドバイスありがとうございます。
    インクピクチャの内容が出力されたPDFに表示されるのは確認しました。
    自分のインテリジェンスが低くて申し訳ないです。まだ、よく分かってないです。

    このコードで線を描画、テキストを入力する場合はどこにどう追記すればよろしいですか

    PDFsharpではPDFのページを作って、そこに線や文字を描画してました。

    アドバイス頂いたコードだと、docに線や文字を描画していくという事でしょうか

    返事が遅れて申し訳ありません。
    2023年3月23日 0:21