[NoBrainer] Automating Sparx Enterprise Architect with PowerShell

It has been some time since I have last posted something on our blog, so I will start with something easy: today I made the switch from Sparx Enterprise Architect v13.5 to v14.0. And yes, it really paid off since I now can actually decipher all those small connector icons on my Surface Book 2 screen which has a resolution of more than 3000px horizontally …

While I was at it I needed to extract a couple of diagrams from a model so I decided to give PowerShell a try and have it automated for me. As it turned out it was quite easy (except for a few things – more on that later).

In EA it all starts with the Object Model which shows you roughly how to get started in C#, Java and some script languages. EA being exposed as an ActiveX COM object for PowerShell loading a repository looks like this:

# preparing the scene 
cd "C:\Program Files (x86)\Sparx Systems\EA";
Add-Type -Path 'C:\Program Files (x86)\Sparx Systems\EA\Interop.EA.dll';

# instantiating the COM object
# NOTE: this will not work reliably
# $ea = [EA.RepositoryClass]::new();
$ea = New-Object -ComObject EA.Repository

# loading the repository
$modelPath = 'C:\MyFolder';
$modelPathAndFileName = Join-Path -Path $modelPath -ChildPath 'MyModel.eap';
$result = $ea.OpenFile($modelPathAndFileName);
#$result must be $true

A repository is also a COM object, so inspecting types is a little bit harder as usual, as all you see is: __ComObject. Each repository has a collection of Models which in turn contain a Packages collection. In each Package you find a Diagrams collection which may hold zero or more diagrams.

Exporting a diagram is limited to PDF, PNG, JPG and EMF via $diagram.SaveAsPDF() and $diagram.SaveImagePage(). The documentation is sparse but enough to start. A speciality with images is that you have to export an image for each page of the diagram whereas with PDF the whole diagram is exported.
This means you have to check on every diagram of how many pages it consists and save them with separate calls:

for($pageWidth = 1; $pageWidth -le $diagram.PageWidth; $pageWidth++)
{
  for($pageHeight = 1; $pageHeight -le $diagram.PageHeight; $pageHeight++)
  {
    $diagramImgPathAndFileName = Join-Path -Path $modelPath -ChildPath ('img\{0}-{1}-x{2}-y{3}.png' -f $diagram.Name, $diagram.DiagramGUID, $pageWidth, $pageHeight);
    $result = $diagram.SaveImagePage($pageWidth, $pageHeight, 0, 0, $diagramImgPathAndFileName, 0)
  }
}

In order to extract all diagrams in a repository we can crawl the package tree recursively as shown in the following script:

# preparing the scene 
cd "C:\Program Files (x86)\Sparx Systems\EA";
Add-Type -Path 'C:\Program Files (x86)\Sparx Systems\EA\Interop.EA.dll';

# instantiating the COM object
# NOTE: this will not work reliably
# $ea = [EA.RepositoryClass]::new();
$ea = New-Object -ComObject EA.Repository

# loading the repository
$modelPath = 'C:\MyFolder';
$modelPathAndFileName = Join-Path -Path $modelPath -ChildPath 'MyModel.eap';
$result = $ea.OpenFile($modelPathAndFileName);
#$result must be $true

function Process-Packages($packages)
{
  if(!$packages) { throw [System.ArgumentNullException]::new('packages'); }
  if(0 -ge $packages.Count) { return; }

  foreach($package in $packages)
  {
    $diagrams = Get-EaDiagrams $package;
    foreach($diagram in $diagrams)
    {
      $diagramePdfPathAndFileName = Join-Path -Path $modelPath -ChildPath ('img\{0}-{1}.pdf' -f $diagram.Name, $diagram.DiagramGUID);
      $result = $diagram.SaveAsPDF($diagramePdfPathAndFileName)

      if(1 -ge $diagram.PageWidth -and 1 -ge $diagram.PageHeight)
      {
        $diagramImgPathAndFileName = Join-Path -Path $modelPath -ChildPath ('img\{0}-{1}.png' -f $diagram.Name, $diagram.DiagramGUID);
        $result = $diagram.SaveImagePage(1, 1, 0, 0, $diagramImgPathAndFileName, 0);
        continue;
      }

      for($pageWidth = 1; $pageWidth -le $diagram.PageWidth; $pageWidth++)
      {
        for($pageHeight = 1; $pageHeight -le $diagram.PageHeight; $pageHeight++)
        {
          $diagramImgPathAndFileName = Join-Path -Path $modelPath -ChildPath ('img\{0}-{1}-x{2}-y{3}.png' -f $diagram.Name, $diagram.DiagramGUID, $pageWidth, $pageHeight);
          $result = $diagram.SaveImagePage($pageWidth, $pageHeight, 0, 0, $diagramImgPathAndFileName, 0)
        }
      }
    }
    Process-Packages $package.Packages;
  }
}

function Get-EaPackages($package)
{
  if(!$package) { throw [System.ArgumentNullException]::new('package'); }

  $result = [System.Collections.ArrayList]::new();

  foreach($item in $package.Packages)
  {
    $null = $result.Add($item);
  }

  return $result;
}

function Get-EaDiagrams($package)
{
  if(!$package) { throw [System.ArgumentNullException]::new('package'); }

  $result = [System.Collections.ArrayList]::new();

  foreach($item in $package.Diagrams)
  {
    $null = $result.Add($item);
  }

  return $result;
}

foreach($model in $ea.models) 
{ 
  Process-Packages $model.packages;
}

$ea.CloseFile();
$ea.Exit();

A downside while exporting images is, that every image has always the size of a full page – regardless its actual size, as you can see in the following image (notice all the white space at the bottom):

Enterprise Architect Class Diagram

Exported via PowerShell

Summary

Automating EA is easy, but the API has a very weak documentation and lacks good samples. Methods are only partially documented and you have to try and error a lot. Doing this in C# does not make things easier as there you have to use the same COM interop library. At least automation is possible …

Make sure to close the file and call Exit() on the repository, otherwise you have leftover EA processes in your system.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

%d bloggers like this: