How to export draft images

by Solution Partner Phenom Solution Partner Phenom on ‎02-09-2015 06:08 AM (5,640 Views)

Have you ever had a need to export vector graphics or raster graphics from a draft file? Over my 15 years of working with Solid Edge, I have found myself in that position many, many times. In this post, I'll cover two possible approaches to accomplishing this task.

 

Background

 

When a Solid Edge draft file is saved, an Enhanced Metafile (EMF) is embedded into the file for each working sheet. The reason Solid Edge does this is so that their lightweight viewer can easily and quickly render a graphical view of the draft. The EMF format is ideal in this scenario as it is lightweight, vector based and scales perfectly when resized rather than distorting like a raster image would.

 

 

If we can aquire these vector images ourselves, a lot of doors open to what we can accomplish. The question is, how do we access these embedded EMF images for our own purposes.

 

For technical details about the EMF format, see the Enhanced Metafile Format Specification.

 

Option 1 - Solid Edge API + a touch of open source

 

If we examine the Solid Edge API, in this case draft.tlb, we will find a CopyEMFToClipboard() method available for a Sheet object.

 

// Generated .IDL file (by the OLE/COM Object Viewer)
// 
// typelib filename: draft.tlb
[
  uuid(3E2B3BDC-F0B9-11D1-BDFD-080036B4D502),
  version(1.0),
  helpstring("Solid Edge Draft Type Library"),
]
library SolidEdgeDraft
{
	[
	  uuid(FFB20FA0-339B-11CE-956A-08003601DFE5),
	  helpstring("Represents a sheet in a document."),
	  helpcontext(0x00007537)
	]
	dispinterface Sheet {
		properties:
		methods:
			[id(0x000000de), helpstring("Copies an enhanced metafile to the operating system clipboard."), helpcontext(0x0000756c)]
			void CopyEMFToClipboard();
	};
};

The CopyEMFToClipboard() method creates an in-memory EMF and places it on the clipboard in CF_ENHMETAFILE format. In order to access the EMF data, we use the GetClipboardData() function which returns a pointer to a HMETAFILE. For most (non C++) programmers, everything comes to a screeching halt right there. Unless you have ever dabbled with C++, you likely have no idea how to proceed.

 

Fortunately, it's 2015 and people actually share code. If you don't want to dig into the gory details of how to access the EMF on the clipboard, you can easily install the SolidEdge.Community NuGet package and leverage pre-built extension methods to gain access to the actual EMF. If you're not familiar with NuGet, no worries, I've got you covered in my How to use NuGet for Solid Edge custom development post. If you're not comfortable using an open source library, no problem! You can view the full source code for SolidEdge.Community on GitHub. Specifically for this article, you'll want to reference SheetExtensions.cs and look for the SaveAsEnhancedMetafile() extension method. Extension methods like this enhance the base Solid Edge API by overlaying additional methods to classes like SolidEdgeDraft.Sheet.

 

For an example of how to use the SaveAsEnhancedMetafile() extension method, head over to the Solid Edge Community on GitHub. There is a C# sample and Visual Basic sample available. The following is a short snippet demonstrating the usage.

 

foreach (SolidEdgeDraft.Sheet sheet in workingSection.Sheets)
{
    // Note: SaveAsEnhancedMetafile() is an extension method from SolidEdge.Community.dll.
    sheet.SaveAsEnhancedMetafile(emfFileName);
}

 

I should also mention that the GetEnhancedMetafile() extension method is also available. This will return a .NET friendly System.Drawing.Imaging.Metafile.

 

Note that in order to enabled these extension methods, you must use the C# using keyword or the Visual Basic Imports keyword.

 

using SolidEdgeCommunity.Extensions;

 or

Imports SolidEdgeCommunity.Extensions

 

Option 2 - Open source only

 

As an Applications Architect, I often find myself with project requirements that don't always agree with existing software APIs. For example, I've needed to process and export 11,000+ draft files as fast as possible. I've also needed to perform this export process in an automated server environment. Automating Solid Edge as shown in Option 1 was not really a (good) option as Solid Edge is a UI application.

 

Considering these requirements, the SolidEdge.Community.Reader NuGet package was born. Full source code can be found on the SolidEdge.Community.Reader GitHub page. Installing this NuGet package into your project adds a reference to the SolidEdge.Community.Reader.dll assembly. The assembly has the ability to read Solid Edge files natively and perform the same export operation without the need to have Solid Edge running.

 

For an example of how to use the SolidEdge.Community.Reader NuGet package, head over to the Solid Edge Community on GitHub. There is a C# sample and Visual Basic sample available. The following is a short snippet demonstrating the usage.

 

// Open the file.
using (var draftDocument = DraftDocument.Open(options.FileName))
{
    // Process each sheet.
    foreach (var sheet in draftDocument.Sheets)
    {
        // Save EMF.
        sheet.SaveAsEmf(emfFileName);
    }
}

In my particular project, using the SolidEdge.Community.Reader NuGet package yielded the following results.

 

  • DFT count: 11,450 (65 files processed \ sec)
  • EMF count: 24,202 (137 files exported \ sec)
  • Duration: 2 minutes 57 seconds

65 DFT files per second! 137 EMF files per second! Now that's what I'm talking about. Honestly, I was a bit mind blown when I saw the results. It goes without saying that a lot of hard work has gone into the SolidEdge.Community.Reader NuGet package. It is an extremely efficient and fast bit of code. 

 

Conclusion

 

In this post, I demonstrated 2 methods of how to access and export an EMF for each working sheet in a draft. I focused on the EMF format but the C# sample and Visual Basic sample also show you how to convert the EMF to other image types.

 

Reasons for the ability to do this include but not limited to:

 

  • Displaying draft files outside of Solid Edge
  • Converting EMFs to other formats for other purposes.
  • Batch printing. *You can render EMFs directly to printers ;-)

More than anything, I want you all to be aware of the usefullness and power of open source. In my opinion, if you're not leveraging and\or contributing to what open source has to offer, you're doing it wrong.

 

 

Comments
by Phenom
on ‎02-10-2015 07:26 PM

jnewell wrote:In my opinion, if you're not leveraging and\or contributing to what open source has to offer, you're doing it wrong.

  I subscribe !!

 

Playing with the intrinsic CopyEMFToClipboard API and found it still copied the background sheet graphics when the Draft document is in an un-saved state.

 

What I really expecting was a completely blank image or best an exception thrown since the file is not yet saved.

 

The details on getting the EMF from the clipboard are indeed gory !!

Thanks for such a detailed article.

 

~Tushar

 

by Phenom
on ‎02-27-2015 05:18 AM

I was trying to write a VB equivalent for this and consistently getting the len as 0

Any inputs are highly appreciated.

Alternatively, is it possible to wrap this tested C# code into a library and call it in my VB.Net application, What would be the steps ?

 

<Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="OpenClipboard", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
        Public Shared Function OpenClipboard(ByVal hWnd As IntPtr) As Boolean
        End Function

        <Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="EmptyClipboard", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
        Public Shared Function EmptyClipboard() As Boolean
        End Function

        <Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="SetClipboardData", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
        Public Shared Function SetClipboardData(ByVal uFormat As Integer, ByVal ByValhWnd As IntPtr) As IntPtr
        End Function

        <Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="CloseClipboard", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
        Public Shared Function CloseClipboard() As Boolean
        End Function

        <Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="GetClipboardData", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
        Public Shared Function GetClipboardData(ByVal uFormat As Integer) As IntPtr
        End Function

        <Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="IsClipboardFormatAvailable", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
        Public Shared Function IsClipboardFormatAvailable(ByVal uFormat As Integer) As Short
        End Function

        <Runtime.InteropServices.DllImport("gdi32.dll")>
        Public Shared Function GetEnhMetaFileBits(ByVal hemf As IntPtr, ByVal cbBuffer As UInteger, ByRef lpbBuffer() As Byte) As UInteger
        End Function

        <Runtime.InteropServices.DllImport("gdi32.dll")>
        Public Shared Function DeleteEnhMetaFile(ByVal hemf As IntPtr) As Boolean
        End Function

        Const CF_ENHMETAFILE As UInt32 = 14

        Public Sub SaveAsEnhancedMetafile(ByVal sheet As SolidEdgeDraft.Sheet, ByVal filename As String)
            sheet.CopyEMFToClipboard()
            If (OpenClipboard(IntPtr.Zero)) Then
                If (IsClipboardFormatAvailable(CF_ENHMETAFILE)) Then
                    Dim hEMF As IntPtr = GetClipboardData(CF_ENHMETAFILE)
                    Dim len As UInteger = GetEnhMetaFileBits(hEMF, 0, Nothing)
                    Dim rawBytes(len) As Byte
                    GetEnhMetaFileBits(hEMF, len, rawBytes)
                    System.IO.File.WriteAllBytes(filename, rawBytes)
                    DeleteEnhMetaFile(hEMF)
                End If
            End If
        End Sub

by Solution Partner Phenom Solution Partner Phenom
on ‎02-27-2015 08:52 AM

I use Instant VB from Tangible Software Solutions to convert from C# to VB. I get about 99% accurate conversion rate using that tool.

 

The code is from the SolidEdge.Community NuGet package and my How to use NuGet video demonstrates how to get and use it.

by Phenom
on ‎02-27-2015 07:43 PM

Amazing, Its 2015 and I was still not aware or think of such a site and facility of conversion would exist.

After getting a couple of required DLL imports manually posrted to VB.Net, the same code in VB.Net gives a 'Out of memory' error when trying to display the Metafile in a picturebox. It works fine in CSharp though. Strange.

 

The following works:

            bool b = OpenClipboard(IntPtr.Zero);
            IntPtr ip = GetClipboardData(14);
            pictureBox1.Image = new Metafile(ip, true);

 

This gives out of memory error:

        Dim b as Boolean = OpenClipboard(IntPtr.Zero)
        Dim ip As IntPtr = GetClipboardData(14)
        pictureBox1.Image = New Metafile(ip, True)

 

by Solution Partner Genius Solution Partner Genius
on ‎03-05-2015 06:22 PM

Thanks Jason this save me lots of time.

 

Is there any way to define image quality (300Dpi) and size when converting it to jpg for example.

 

I got an Add-in that create jpg on save that works great thanks to all your work but image are merly 1.5 Mo.

by Solution Partner Phenom Solution Partner Phenom
on ‎03-06-2015 02:37 PM

@rbertin I'll look at it over the weekend and see what I can come up with.

by Phenom
‎03-09-2015 12:14 PM - edited ‎03-09-2015 12:15 PM

rbertin, what method are you using for converting the metafile to a jpeg ?

Though not an efficient way, are you looking for something like this:

 

using (Bitmap bitmap = (Bitmap)Image.FromFile("file.jpg"))
    {        using (Bitmap newBitmap = new Bitmap(bitmap))
        {            newBitmap.SetResolution(300, 300);            newBitmap.Save("file300.jpg", ImageFormat.Jpeg);
        }
    }
by Solution Partner Phenom Solution Partner Phenom
on ‎03-09-2015 12:44 PM

Tweaking the sample from Resizing an Image On-The-Fly using .NET, I think you can get close to what you're after.

// http://www.codeproject.com/Articles/191424/Resizing-an-Image-On-The-Fly-using-NET
public static Image ResizeImage(Image image, Size size, bool preserveAspectRatio = true)
{
    int newWidth;
    int newHeight;
    if (preserveAspectRatio)
    {
        int originalWidth = image.Width;
        int originalHeight = image.Height;
        float percentWidth = (float)size.Width / (float)originalWidth;
        float percentHeight = (float)size.Height / (float)originalHeight;
        float percent = percentHeight < percentWidth ? percentHeight : percentWidth;
        newWidth = (int)(originalWidth * percent);
        newHeight = (int)(originalHeight * percent);
    }
    else
    {
        newWidth = size.Width;
        newHeight = size.Height;
    }
    var newImage = new Bitmap(newWidth, newHeight);
    newImage.SetResolution(300, 300);

    using (Graphics graphicsHandle = Graphics.FromImage(newImage))
    {
        graphicsHandle.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
        graphicsHandle.DrawImage(image, 0, 0, newWidth, newHeight);
    }
    return newImage;
}

 and I called it like this:

var image = ResizeImage(metafile, new Size(1920, 1080), true);
image.Save(jpgFileName, ImageFormat.Jpeg);

The main factor that I'm seeing in reducing file size is resizing the image. The dimensions of the EMF I'm testing with is 9922 x 7016. With the code change, the dimensions are 1527 x 1080. Note that I modified the article example by using SetResolution as you requested.

by Solution Partner Genius Solution Partner Genius
‎03-10-2015 04:40 AM - edited ‎03-10-2015 04:56 AM

Thanks @Tushar and @jnewell this works great with this method.

by Creator
on ‎04-07-2015 08:52 AM

Hi Jason

Thank you very much for all your effort to make programming for solid edge better and faster!

In the C# or VB Examples i have troubles with:

C#

var options = new Options();

or in VB:

Dim options = New Options()

 

What type is Options()?

Have I forgotten a Reference?

All Nuget Installations are successfull.

 

Greetings from Switzerland

Matthias Leemann

by Solution Partner Phenom Solution Partner Phenom
on ‎04-10-2015 11:13 AM

Everything is in GitHub.

 

by Creator
on ‎04-13-2015 02:45 AM

Thank you Jason, now a have all files downloaded from GitHub.

by Creator
on ‎07-29-2015 05:22 PM

Hi Jason

I like the SolidEdge.Community.Reader to read closed draft files!

Now another question: It's possible to read closed assembly files? I like to read all occurences of an assembly with properties (par example: ExcludeFromBOM) of each occurence.

I can see some code, but it looks like in work...

 

SolidEdge.Community.Reader/src/SolidEdge.Community.Reader/Assembly/Attachments.cs

by Solution Partner Phenom Solution Partner Phenom
on ‎07-30-2015 12:52 PM

@MatthiasLeemann it's currently not possible. The in-work code that you saw was me trying to figure out the assembly structure (links). The idea of a lightweight .NET reader is a good one but there are limits to what we can accomplish on our own since the internal file structures are not documented.

by Experimenter
on ‎08-04-2016 09:48 PM

Hi,

 

Ive been searching for a way to export drafts into vector images. This post is about the closest Ive found so far, but Im completely lost. Ive followed all of your instructions, watched the videos and When I past the SheetExtensions.CS into VS and run it, I get errors.

 

Im not a coder and I really dont have the time to sit down and learn all of the intricacies of VB or C++ or C# etc etc. I do love open source as it allows me to increase my capacity without having to learn this. But when errors pop up, I may as well throw the whole thing out the window.

 

At this point in time, im looking to simply export a solid edge draft into a vector image. I was hoping your post would help here, but im afraid it has done more to plug your visual basic prowess and the extensions you made (of which I downloaded and Im sure in time will be put to good use) than to actually step people through the process of export a solid edge draft.

 

If you have the patience and time, could you step the un initiated through the visual basic process to achieve an outcome?  I really need help with this one task so I can proceed with my project. 

 

If you dont know or arent willing to help, maybe you could point me to some places that would better be suited to achieve this?

 

non the less, I thankyou for your post. Im sure Ill get more out of it later when I have the time to sit down a really work through it.

 

Cheers

 

jeddel

by Siemens Phenom Siemens Phenom
on ‎08-05-2016 02:12 AM

@jeddell if you are not a programmer and need to translate files then consider using the Solid Edge command line translator to accomplish your task:

 

Translating Solid Edge Files from the Command Line

by Dreamer
a week ago

Hey,

 

thank you very much for this post!

it is very helpful.

 

is there a way to create an EMF for a specific Drawing View object (in the sheet) ?

 

by Solution Partner Phenom Solution Partner Phenom
a week ago

Not to my knowledge. EMF are GDI+ records so I suppose, in theoery, one could define a rectangle and extract out the GDI+ records contained within and generate a new EMF. I've never personally tried it though.

by Dreamer
Thursday

Thank you for the quick answer!

 

I want to explain what i did, hope maybe you will have another idea for me.

 

 

// Open the file.
using (var draftDocument = DraftDocument.Open(options.FileName))
{
    // Process each sheet.
    foreach (var sheet in draftDocument.Sheets)
    {
        
        foreach (var currDrew in sheet.DrawingViews)
	{
     		if (currDrew.DrawingViewType == SolidEdgeDraft.SrawingViewTypeConstants.isIsometricView)
		{
			/***** Now i want to create an image from the currDew***/
		}
	}

     }
}

 

-i tried to create an emf from the currDrew object

-i tried to create new sheet that will contain only the currDrew and then create the EMF.

but nothing worked for me.

 

I checked in the solid edge program, and it is possible to do it there

(need to choose the isometric draw -> right click -> Draw in View -> save as....)

 

Thank you a lot!!

Labels
Top Kudoed Posts