Laden...

ZedGraph mit itextSharp als Vektorgrafik(EMF bzw. WMF) in PDF einbinden (VB.Net-Code)

Erstellt von jonny_dotnet vor 11 Jahren Letzter Beitrag vor 11 Jahren 6.750 Views
J
jonny_dotnet Themenstarter:in
2 Beiträge seit 2012
vor 11 Jahren
ZedGraph mit itextSharp als Vektorgrafik(EMF bzw. WMF) in PDF einbinden (VB.Net-Code)

Beschreibung:
Hallo zusammen, ich habe keine Frage sondern will euch meinen VB.net-Code vorstellen, da ich sicher bin, dass er auch einmal von anderen Entwicklern gebraucht wird.

Ich habe eine Möglichkeit gefunden, Diagramme die mit Hilfe der Zedgraph-opensource-libary (https://sourceforge.net/projects/zedgraph/) erstellt wurden mit Hilfe des vektorbasierten Microsoft Formats "Enhanced Metafile (EMF)" bzw. "Windows Metafile (WMF)" in eine PDF zu exportieren. Dabei wird die itextsharp-opensouce-libary (https://sourceforge.net/projects/itextsharp/) verwendet. Ich arbeite zwar mit VB.net aber benutze ja C# Bibiotheken, deshalb habe ich mir dieses Forum ausgesucht, um mit euch meinen Code zu teilen.
Bisher habe ich Zedgraph-Diagramme immer erst als Bitmap exportiert:


Dim bmp As New System.Drawing.Bitmap(1, 1)
bmp = zedGraphControl1.GraphPane.GetImage
bmp.Save("C:\img_" & zedGraphControl1.Name & ".bmp")

und anschließend habe ich das Bitmap in die PDF wie folgt eingebunden,


Imports ZedGraph
Imports iTextSharp.text
Imports iTextSharp.text.pdf
...
Private Sub Bmp2Pdf() 
    Dim bmp_iText As iTextSharp.text.Image = _
         Image.GetInstance("C:\img_" & zedGraphControl1.Name & ".bmp")
    'Create a pdf with itextsharp-libary:
    Dim pdfdoc As New iTextSharp.text.Document
    Dim writer As iTextSharp.text.pdf.PdfWriter
    Dim pdfPath As String = "C:\test.pdf"
    'Prepare Filestream with UserDef-Path
    Dim fs As IO.FileStream = New IO.FileStream(pdfPath, IO.FileMode.Create)
    'Prepare instance of PDF to write into it
    writer = iTextSharp.text.pdf.PdfWriter.GetInstance(pdfdoc, fs)
    pdfdoc.Add(bmp_iText)
End Sub

Aber in diesem Fall kann ich das ZedGraph Diagramm nur als Pixelgrafik betrachten, was sehr unschön ist. Zur Lösung des Problems muss zunächst die "Zedgraph.MasterPane" als Enhanced Metafile (EMF) exportiert werden:


Private Sub ZedGraph2Emf() 
'Built Metafile from Graph
Dim emfTempFileFullPath As String = "C:\img_" & zedGraphControl1.Name & ".emf"
Using g As Graphics = Me.CreateGraphics()
    Dim hdc As IntPtr = g.GetHdc
    Dim metafile As System.Drawing.Imaging.Metafile = _
        New System.Drawing.Imaging.Metafile(emfTempFileFullPath, hdc, _ 
        System.Drawing.Imaging.EmfType.EmfPlusDual)
    Using gMeta As Graphics = Graphics.FromImage(metafile)
        zedGraphControl1.MasterPane.Draw(gMeta) 'Draw ZGraph in EMF
    End Using
    'Destroy object
    metafile.Dispose()
End Using
End Sub

Mit der itextsharp-libary kann leider kein EMF-File in eine PDF eingebunden werden, aber zum Glück das "altmodische" Windows Meta File (WMF)-Format. Deshalb ist es nun notwendig, die erzeugte EMF in eine WMF umzuwandeln. Dies kann durch die von Windows bereitgestellten API GDI-Funktionen realisiert werden. Hierfür habe ich folgende Funktion entwickelt:


Public Function emf2wmf(ByVal emfTempFileFullPath As String, _
      ByVal wmfTempFileFullPath As String) As Boolean
'Check existence of EMF-File
If Not IO.File.Exists(emfTempFileFullPath) Then
    MsgBox("EMF-File does not exist.", MsgBoxStyle.Exclamation)
    Return False
End If
'Construct a Metafile object from an existing EMF disk file
Using fileStreamEmf As IO.FileStream = _
                New IO.FileStream(emfTempFileFullPath,IO.FileMode.Open)
    Using streamReaderEmf As IO.StreamReader = _
            New IO.StreamReader(fileStreamEmf)
        Using streamEmf As System.IO.Stream = streamReaderEmf.BaseStream
            'Get Metafile
            Using mf As New System.Drawing.Imaging.Metafile(streamEmf)
                ' Get Handle of Metafile
                Dim enhMetafileHandle As IntPtr = mf.GetHenhmetafile()
                Dim h As IntPtr
                'A call to the GetEnhMetaFileBits API-Function (gdiplus.dll, 
                'part of gdi32.dll) with a null buffer return the size of 
                'the buffer need to store the WMF bits.
                Dim bufferSize As UInteger = _
                    API.GetEnhMetaFileBits(enhMetafileHandle, 0, h)
                'Create an array to hold the bits
                Dim buffer(CInt(bufferSize)) As Byte
                'Use the GetEnhMetaFileBits API-Function (gdiplus.dll, 
                'part of gdi32.dll) to convert the Emf to Wmf
                API.GdipEmfToWmfBits(enhMetafileHandle, bufferSize, _
                    buffer, API.MM_ANISOTROPIC, _
                    API.EmfToWmfBitsFlags.EmfToWmfBitsFlagsIncludePlaceable)
                'Save WMF-File with Memory-Stream-Object
                Using msMetafileStream As New System.IO.MemoryStream
                    msMetafileStream.Write(buffer, 0, CInt(bufferSize))
                    Dim baMetafileData() As Byte
                    baMetafileData = msMetafileStream.ToArray
                    'Write Bytes to File, use fileStream
                    Using fileStreamWmf As System.IO.FileStream = _
                       New IO.FileStream(wmfTempFileFullPath, _
                           IO.FileMode.OpenOrCreate, _
                           IO.FileAccess.ReadWrite, IO.FileShare.None)
                       For idx As Long = 0 To baMetafileData.Length - 1
                           fileStreamWmf.WriteByte(baMetafileData(idx))
                       Next idx
                    End Using 'fileStream
                End Using 'msMetafileStream
            End Using 'mf
        End Using 'streamEmf
    End Using 'streamReaderEmf
End Using 'fileStreamEmf
Return True
End Function

Nach der Konvertierung von EMF zu WMF mit dem Aufruf...


emf2wmf("C:\img_" & zedGraphControl1.Name & ".emf", _
                            "C:\img_" & zedGraphControl1.Name & ".wmf") 

...ist es nun möglich die WMF der PDF hinzuzufügen:


...
Dim wmf As iTextSharp.text.ImgWMF = _
  iTextSharp.text.Image.GetInstance("C:\img_" & zedGraphControl1.Name & ".wmf")
pdfdoc.Add(wmf)

Die genutzten GDI-Functions werden über eine erstellte Klasse mit der Bezeichnung Class "API" aufgerufen, die auf dem Beitrag von Convert an image into WMF with .NET? aufbaut und von C# nach VB.Net übersetzt wurde:


Public Class API
    Public Enum EmfToWmfBitsFlags
        'Use the default conversion 
        EmfToWmfBitsFlagsDefault = 0
        'Embedded the source of the EMF metafiel within the resulting WMF 
        'metafile 
        EmfToWmfBitsFlagsEmbedEmf = 1
        'Place a 22-byte header in the resulting WMF file. The header is 
        'required for the metafile to be considered placeable. 
        EmfToWmfBitsFlagsIncludePlaceable = 2
        'Don't simulate clipping by using the XOR operator. 
        EmfToWmfBitsFlagsNoXORClip = 4
    End Enum
    'Ensures that the metafile maintains a 1:1 aspect ratio 
    Public Const MM_ISOTROPIC% = 7
    'Allows the x-coordinates and y-coordinates of the metafile to be adjusted 
    'independently 
    Public Const MM_ANISOTROPIC% = 8
<System.Runtime.InteropServices.DllImport("gdiplus.dll", EntryPoint:="GdipEmfToWmfBits")> _
    Public Shared Function GdipEmfToWmfBits(<System.Runtime.InteropServices.InAttribute()> _
        ByVal hEmf As System.IntPtr, ByVal bufferSize As UInteger, ByVal buffer() As Byte, _
        ByVal mappingMode As Integer, ByVal flags As EmfToWmfBitsFlags) As UInteger
    End Function
<System.Runtime.InteropServices.DllImportAttribute("gdi32.dll", EntryPoint:="GetEnhMetaFileBits")> _
    Public Shared Function GetEnhMetaFileBits(<System.Runtime.InteropServices.InAttribute()> _
        ByVal hEMF As System.IntPtr, ByVal nSize As UInteger, ByVal lpData As IntPtr) As UInteger
    End Function
End Class

Anbei habe ich ein Großteil meines Quellcodes angehängt. (Das komplette Projekt inkl. Bibiothekten wäre zu groß gewesen.)
Ich hoffe das jemand von euch von meinem Beitrag profitieren kann, bzw. ähnliches vorhat und hiermit eine Lösung gefunden hat.

Beste Grüße,
Jonny

PS: Mein Beitrag im ZedGraph-Forum und eine lauffähiges VB.net Beispielprojekt zum downloaden findet sich hier: sourceforge.net: ZedGraph / Discussion / Help:Saving graph as PDF

Schlagwörter: ZedGraph, itextsharp, EMF, WMF, PDF, API, GDI, GDI+,GDLplus, GDI32, emf2wmf, emftowmf, GdipEmfToWmfBits, GetEnhMetaFileBits, ZedGraph in vektorformat, Diagramm als PDF, opensource-Bibiotheken, VB.net

J
jonny_dotnet Themenstarter:in
2 Beiträge seit 2012
vor 11 Jahren

Hallo nochmal,
ich musste die ZedGraph2Emf-Subroutine nochmals ändern, da sonst beim Zoomen das Metafile nicht richtig erstellt wird. Details dazu finden sich hier: sourceforge.net: ZedGraph / Discussion / Help:EMF bug and feature (with code!). Der geänderte Code lautet somit:


Private Sub ZedGraph2Emf() 
'Built Metafile from Graph
Dim emfTempFileFullPath As String ="C:\img_" & _
    zedGraphControl1.Name & ".emf"
Using g As Graphics = Me.CreateGraphics()
    Dim hdc As IntPtr = g.GetHdc
    Dim metafile As System.Drawing.Imaging.Metafile = New _
      System.Drawing.Imaging.Metafile(emfTempFileFullPath, _
      hdc, zedGraphControl1.MasterPane.Rect, _
      System.Drawing.Imaging.MetafileFrameUnit.Pixel, _
      System.Drawing.Imaging.EmfType.EmfPlusDual)
    Using gMeta As Graphics = Graphics.FromImage(metafile)
        'Draw ZGraph in EMF
        zedGraphControl1.MasterPane.Draw(gMeta) 
    End Using
    'Destroy object
    metafile.Dispose()
End Using
End Sub

Gruß Jonny