OfficeIMO.Visio 1.0.2

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package OfficeIMO.Visio --version 1.0.2
                    
NuGet\Install-Package OfficeIMO.Visio -Version 1.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="OfficeIMO.Visio" Version="1.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="OfficeIMO.Visio" Version="1.0.2" />
                    
Directory.Packages.props
<PackageReference Include="OfficeIMO.Visio" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add OfficeIMO.Visio --version 1.0.2
                    
#r "nuget: OfficeIMO.Visio, 1.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package OfficeIMO.Visio@1.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=OfficeIMO.Visio&version=1.0.2
                    
Install as a Cake Addin
#tool nuget:?package=OfficeIMO.Visio&version=1.0.2
                    
Install as a Cake Tool

OfficeIMO.Visio — .NET Visio Utilities

OfficeIMO.Visio provides helpers for creating and editing .vsdx drawings with Open XML.

  • Targets: netstandard2.0, net472 (Windows), net8.0, net10.0
  • License: MIT
  • NuGet: OfficeIMO.Visio
  • Dependencies: OfficeIMO.Drawing, System.IO.Packaging, Microsoft.Bcl.AsyncInterfaces (net472)

Install

dotnet add package OfficeIMO.Visio

Quick sample (fluent)

using OfficeIMO.Visio;
using OfficeIMO.Visio.Fluent;

var vsd = VisioDocument.Create("diagram.vsdx");
vsd.AsFluent()
   .Info(i => i.Title("Demo").Author("You"))
   .Page("Page-1", p => p
       .Title("Demo Flow")
       .Rect("start", 1, 1, 2, 1, "Start")
       .Diamond("decision", 4, 1.5, 2, 2, "Decision")
       .Ellipse("end", 7, 1.5, 2, 1, "End")
       .Connect("start", "decision", VisioSide.Right, VisioSide.Left,
           c => c.RightAngle().ArrowEnd(EndArrow.Triangle))
       .Connect("decision", "end", VisioSide.Right, VisioSide.Left,
           c => c.RightAngle().ArrowEnd(EndArrow.Triangle).Label("Yes")))
   .End();
vsd.Save();

Quick sample (editing an existing diagram)

using System;
using OfficeIMO.Visio;
using OfficeIMO.Visio.Fluent;
using OfficeIMO.Visio.Stencils;
using Color = OfficeIMO.Drawing.OfficeColor;

VisioDocument.Load("operations.vsdx")
    .AsFluent()
    .ExistingPage("Operations", page => page
        .ShapesWithData("Owner", "Ops", selection => selection
            .Fill(Color.LightBlue)
            .ShapeData("Reviewed", "Yes", "Reviewed", VisioShapeDataType.Boolean))
        .ShapesContainingText("Legacy", selection => selection
            .Text(shape => shape.Text!.Replace("Legacy", "Production", StringComparison.Ordinal)))
        .Rect("monitoring", 7, 4, 1.4, 0.8, "Monitoring")
        .Connect("api", "monitoring", VisioSide.Right, VisioSide.Left, connector => connector
            .RightAngle()
            .ArrowEnd(EndArrow.Triangle)
            .Label("metrics"))
        .Connectors(selection => selection.LineColor(Color.DarkBlue)))
    .End()
    .Save("operations.updated.vsdx");

For create-or-edit workflows, use PageOrAdd(...) to configure an existing page when it is present or create it with the same fluent page API when it is missing. The fluent selection helpers reuse the typed selection model, so bulk style, Shape Data, layer, hyperlink, and geometry edits stay AOT-friendly and do not require dynamic dispatch or reflection.

Loaded-page fluent editing also supports higher-level diagram queries by shape id: contained/intersecting geometry, connected components, shortest paths, and incoming/outgoing/attached connector selections. That keeps common load-edit-save workflows readable even when the update is based on topology or layout rather than a single known shape id.

Native container membership can be maintained the same way. OfficeIMO updates the typed member/owner graph and writes fresh Visio DEPENDSON(...) relationships on save, so loaded containers can be refit without hand-editing ShapeSheet formulas:

VisioDocument.Load("workflow.vsdx")
    .AsFluent()
    .ExistingPage("Workflow", page => page
        .Rect("cache", 6, 4, 1.3, 0.7, "Cache")
        .AddToContainer("runtime-tier", new[] { "cache" }, options => {
            options.Margin = 0.35;
            options.HeadingHeight = 0.3;
        })
        .RemoveFromContainer("runtime-tier", new[] { "legacy-db" })
        .RelayoutContainerMembers("runtime-tier", layout => {
            layout.Columns = 1;
            layout.VerticalSpacing = 0.25;
        })
        .ConfigureContainer("runtime-tier", options => {
            options.Margin = 0.4;
            options.HeadingHeight = 0.3;
            options.NoRibbon = true;
            options.ShapeStyle = VisioStyleTheme.Technical().Container;
        }, refit: true)
        .RefitContainer("runtime-tier"))
    .End()
    .Save("workflow.updated.vsdx");

ContainerInfo(...) returns a typed snapshot of native container membership, margin, heading height, auto-resize/lock flags, Visio style identifiers, and current visual style. ConfigureContainer(...), ApplyContainerOptions(...), and StyleContainer(...) update loaded containers by id and write the native container User cells back into the VSDX package. OfficeIMO stores heading height as an OfficeIMO User cell alongside Visio's native margin cell, so metric-page containers can be refit after load without shrinking margins through unit conversion drift.

Native Visio comments are also available as typed page state. OfficeIMO writes the real /visio/comments.xml part, so comments survive normal Visio editing instead of being modeled as decorative shapes:

VisioDocument.Load("workflow.vsdx")
    .AsFluent()
    .ExistingPage("Workflow", page => page
        .CommentShape("review", "Confirm approval owner", "Operations", "OP")
        .UpdateComment(1, "Approval owner confirmed", DateTimeOffset.UtcNow)
        .ResolveComment(1, DateTimeOffset.UtcNow)
        .Comment("Reviewed before release", "Operations", "OP"))
    .End()
    .Save("workflow.commented.vsdx");

Existing shapes can be standardized to generated or package-backed stencils from the same fluent loaded-page chain. Replacement preserves placement, text, style, layers, Shape Data, User cells, hyperlinks, and connector endpoints:

VisioDocument.Load("workflow.vsdx")
    .AsFluent()
    .ExistingPage("Workflow", page => page
        .ReplaceMaster("review", VisioStencils.Flowchart.Get("decision"), resizeToMaster: true)
        .ReplaceMastersByMaster("Process", VisioStencils.Flowchart.Get("preparation"), resizeToMaster: true))
    .End()
    .Save("workflow.standardized.vsdx");

For larger cleanup passes, a typed migration map lets you standardize whole families of loaded shapes by current stencil id, master name, shape NameU, or a strongly typed predicate. Rules are evaluated in order, so specific exceptions can sit above broader migrations. Replacement stencils can be supplied directly or resolved from a first-party or package-backed catalog by query:

VisioStencilMigrationMap migration = VisioStencilMigrationMap.Create(map => map
    .MapStencilId("flow.process", VisioStencils.Infrastructure, new[] { "host", "server" }, resizeToStencil: true)
    .MapMaster("Data", VisioStencils.DataPlatform, "relational", resizeToStencil: true)
    .MapNameU("LegacyServer", VisioStencils.Network, "server", resizeToStencil: true));

VisioDocument.Load("legacy-workflow.vsdx")
    .AsFluent()
    .ExistingPage("Workflow", page => page.ApplyStencilMigration(migration))
    .End()
    .Save("workflow.migrated.vsdx");

When you need an approval step before changing a large diagram, plan the same map first. Planning is non-mutating and produces a stable text report for PRs, CI logs, or operator review. Save the report as an artifact when approval happens in another process, then load the approved plan and apply it with the map when you want OfficeIMO to verify that pages, shapes, match rules, and replacement stencils still match the report before mutating the loaded diagram:

VisioDocument legacy = VisioDocument.Load("legacy-workflow.vsdx");
VisioStencilMigrationPlan plan = legacy.PlanStencilMigration(migration);
Console.WriteLine(plan.ToText());
plan.SaveText("legacy-workflow.migration-plan.txt");

if (plan.HasChanges) {
    VisioStencilMigrationPlan approved =
        VisioStencilMigrationPlan.LoadText("legacy-workflow.migration-plan.txt");
    legacy.ApplyStencilMigration(approved, migration);
    legacy.Save("legacy-workflow.migrated.vsdx");
}

For common cleanup of unstenciled/basic flowchart-like diagrams, use the conservative preset. It upgrades shapes such as Rectangle, Diamond, Parallelogram, Hexagon, and Ellipse to semantically matching catalog stencils, while leaving shapes that already carry OfficeIMO stencil metadata alone:

VisioDocument legacyFlow = VisioDocument.Load("legacy-flow.vsdx");
VisioStencilMigrationResult result = legacyFlow.ApplyStencilMigration(
    VisioStencilMigrationPresets.BasicFlowchart(VisioStencils.Flowchart));
legacyFlow.Save("legacy-flow.stenciled.vsdx");
Console.WriteLine($"Migrated {result.Count} shapes.");

The same preset model covers labeled network/infrastructure, architecture, swimlane/process-map, cloud infrastructure, security/identity, Kubernetes/container, data/platform, collaboration/business-process, org-chart, timeline, and sequence cleanup. These presets read common shape text/name cues such as server, database, firewall, switch, gateway, queue, region, storage, lane, phase, serverless function, identity provider, conditional access, container image, data lake, event stream, approval, business application, executive, milestone, release, participant, activation, and fragment, resolve the best matching stencil in the chosen catalog, and still skip shapes already tagged with OfficeIMO stencil metadata:

VisioDocument network = VisioDocument.Load("legacy-network.vsdx");
VisioStencilMigrationPlan networkPlan = network.PlanStencilMigration(
    VisioStencilMigrationPresets.NetworkInfrastructure(VisioStencils.Network));
Console.WriteLine(networkPlan.ToText());

network.ApplyStencilMigration(
    networkPlan,
    VisioStencilMigrationPresets.NetworkInfrastructure(VisioStencils.Network));
network.Save("legacy-network.stenciled.vsdx");

VisioDocument architecture = VisioDocument.Load("legacy-architecture.vsdx");
architecture.ApplyStencilMigration(
    VisioStencilMigrationPresets.ArchitectureInfrastructure(VisioStencils.Architecture));
architecture.Save("legacy-architecture.stenciled.vsdx");

VisioDocument roadmap = VisioDocument.Load("legacy-roadmap.vsdx");
roadmap.ApplyStencilMigration(
    VisioStencilMigrationPresets.Timeline(VisioStencils.Timeline));
roadmap.Save("legacy-roadmap.stenciled.vsdx");

VisioDocument cloud = VisioDocument.Load("legacy-cloud.vsdx");
cloud.ApplyStencilMigration(
    VisioStencilMigrationPresets.CloudInfrastructure(VisioStencils.Cloud));
cloud.Save("legacy-cloud.stenciled.vsdx");

VisioDocument security = VisioDocument.Load("legacy-security.vsdx");
security.ApplyStencilMigration(
    VisioStencilMigrationPresets.SecurityIdentity(VisioStencils.SecurityIdentity));
security.Save("legacy-security.stenciled.vsdx");

VisioDocument platform = VisioDocument.Load("legacy-platform.vsdx");
platform.ApplyStencilMigration(
    VisioStencilMigrationPresets.ContainersKubernetes(VisioStencils.ContainersKubernetes));
platform.ApplyStencilMigration(
    VisioStencilMigrationPresets.DataPlatform(VisioStencils.DataPlatform));
platform.ApplyStencilMigration(
    VisioStencilMigrationPresets.CollaborationBusiness(VisioStencils.CollaborationBusiness));
platform.Save("legacy-platform.stenciled.vsdx");

Quick sample (diagram builder)

using System;
using OfficeIMO.Visio;
using OfficeIMO.Visio.Diagrams;

VisioDocument.Create("flowchart.vsdx")
    .Flowchart("Property buying Flowchart", flow => flow
        .Title()
        .Layout(VisioFlowchartLayout.TwoColumnContinuation)
        .RouteBranches(laneSpacing: 0.5)
        .Start("start", "Start with an agent\nyou trust")
        .Step("consult", "Consult with agent to\ndetermine your property\nwants and needs")
        .Step("market", "With agent, analyze\nmarket to choose\nproperties of interest")
        .OffPage("jump", "A")
        .Continue("resume", "A")
        .Step("offer", "Select ideal property\nand write offer to\npurchase")
        .Decision("agreement", "Negotiate\n& Counteroffer:\nAgreement?")
        .Step("contract", "Accept the contract")
        .End("close", "Close on the\nproperty")
        .Branch("agreement", "No", "market")
        .Callout("agreement", "retry-note", "Loop back if the offer is rejected", VisioSide.Right))
    .Save();

The diagram builder creates normal Visio pages, semantic flowchart shapes, masters, side-glued connectors, labels, deterministic layouts, and routed branch/loop connectors. Flowcharts can also add semantic callouts with leader connectors for complex branches or reviewer notes, either by exact coordinates or by placing notes beside a target node. It is the first high-level authoring layer above the lower-level page/shape APIs.

Quick sample (block diagram builder)

using OfficeIMO.Visio;
using OfficeIMO.Visio.Diagrams;

VisioDocument.Create("block-diagram.vsdx")
    .BlockDiagram("Block Diagram", diagram => diagram
        .Title()
        .Legend()
        .Region("processor", "Processor", 1, 2, 2, 2)
        .Block("input", "Input Device", 0, 2)
        .EmphasisBlock("memory", "Memory Unit", 1, 2)
        .Block("storage", "Secondary\nStorage", 1, 0, VisioBlockShapeKind.Data)
        .Block("control", "Control Unit", 1, 3)
        .Block("alu", "Arithmetic &\nLogic Unit", 1, 4)
        .Block("output", "Output Device", 3, 2)
        .DataFlow("input", "memory")
        .DataFlow("memory", "output")
        .ControlFlow("control", "output", "Control Flow")
        .Callout("memory", "memory-note", "Central shared state", VisioSide.Top))
    .Save();

The block diagram builder creates grid-positioned blocks, light background regions, solid data-flow connectors, dashed control-flow connectors, labels, optional presentation titles/legends, semantic callouts, and master-backed Visio shapes. Callouts can be placed by coordinates or relative to a block side.

Quick sample (dependency diagram builder)

using OfficeIMO.Visio;
using OfficeIMO.Visio.Diagrams;

VisioDocument.Create("dependencies.vsdx")
    .DependencyDiagram("Service Dependencies", diagram => diagram
        .Title()
        .Theme(VisioStyleTheme.Fluent())
        .External("users", "Users")
        .Component("web", "Web App")
        .Component("api", "API")
        .Decision("policy", "Policy")
        .Data("database", "Database")
        .DependsOn("users", "web", "HTTPS")
        .DependsOn("web", "api")
        .ControlDependency("api", "policy", "Authorize")
        .DataDependency("api", "database", "SQL")
        .Callout("policy", "policy-note", "Authorization gates access to data", VisioSide.Top))
    .EnsureVisualQuality(new VisioDiagramQualityOptions {
        CheckConnectorShapeIntersections = false,
        CheckConnectorLabelShapeOverlaps = false
    })
    .Save();

The dependency diagram builder creates deterministic layered DAG layouts from nodes and directed relationships. It automatically grows the page, places component/data/external/decision nodes, routes dependencies, supports semantic coordinate or side-placed callouts, and rejects cycles.

Quick sample (graph diagram builder)

using System;
using System.IO;
using System.Linq;
using OfficeIMO.Visio;
using OfficeIMO.Visio.Diagrams;
using OfficeIMO.Visio.Stencils;
using Color = OfficeIMO.Drawing.OfficeColor;

var installed = VisioStencilPackageCatalog.DiscoverInstalledVisioPackages()
    .Where(path => Path.GetFileName(path).StartsWith("AZURE", StringComparison.OrdinalIgnoreCase));
var stencils = VisioStencilPackageCatalog.LoadMany(installed,
    new VisioStencilPackageLoadOptions {
        IncludeUnsupportedMasters = true
    });

VisioDocument.Create("graph.vsdx")
    .GraphDiagram("Event-driven graph", graph => graph
        .Title()
        .Layout(VisioGraphLayout.Layered)
        .Direction(VisioGraphDirection.LeftToRight)
        .StencilNode("gateway", "API", stencils.Search("API Management").First())
        .StencilNode("events", "Events", stencils.Search("Event Grid").First())
        .Node("worker", "Worker")
        .Node("database", "Database", VisioGraphNodeKind.Data)
        .NodeShapeData("gateway", "Owner", "Platform", "Owner",
            VisioShapeDataType.String, "Owning support team")
        .NodeHyperlink("gateway",
            "https://learn.microsoft.com/azure/api-management/", "API docs")
        .NodeShapeData("database", "Classification", "Confidential",
            "Data classification", VisioShapeDataType.String)
        .NodeStyle("worker", style => {
            style.FillColor = Color.FromRgb(73, 80, 87);
            style.LineColor = Color.FromRgb(45, 52, 59);
        })
        .Zone("runtime", "Runtime", "gateway", "events", "worker")
        .Root("gateway")
        .ControlEdge("gateway-publishes-events", "gateway", "events", "publish")
        .EdgeShapeData("gateway-publishes-events", "Protocol", "HTTPS",
            "Protocol", VisioShapeDataType.String)
        .EdgeHyperlink("gateway-publishes-events",
            "https://learn.microsoft.com/azure/event-grid/", "Event docs")
        .Edge("events", "worker", "trigger")
        .DataEdge("worker-writes-database", "worker", "database", "write")
        .EdgeShapeData("worker-writes-database", "Port", "1433",
            "Port", VisioShapeDataType.Number)
        .EdgeHyperlink("worker-writes-database",
            "https://example.org/contracts/write-model", "Write contract")
        .EdgeStyle("worker-writes-database", style => {
            style.LineColor = Color.FromRgb(0, 102, 204);
            style.LineWeight = 0.026D;
        })
        .DataEdge("database", "gateway", "read model"))
    .Save();

The generic graph builder is for real node/edge maps that are not strict DAGs or one diagram domain. It supports layered, grid, and radial layouts; directed and undirected edges; cycles; disconnected components; background zones; native nodes; and source-aware VisioStencilShape nodes loaded from installed Visio or external .vssx/.vstx packages. Use NodeShapeData and NodeHyperlink to keep generated graph nodes searchable, inspectable, and linked to runbooks, dashboards, API docs, or data catalogs inside Visio. Named edges can also carry connector Shape Data with EdgeShapeData and hyperlinks with EdgeHyperlink, which is useful for protocols, ports, trust levels, API contracts, message schemas, queries, and relationship-specific runbooks. Use NodeStyle and EdgeStyle for local visual emphasis without cloning or forking a whole theme.

For data-driven diagrams, import simple node and edge records. Missing edge IDs are derived from endpoint IDs and connector kind, so regenerated diagrams keep diff-friendly connector identity:

var idp = new VisioGraphNodeRecord("idp", "Entra ID") {
    StencilCatalog = VisioStencils.SecurityIdentity,
    IsRoot = true
};
idp.StencilQueries.Add("idp");
idp.ShapeData.Add("Owner", "IAM");

var cluster = new VisioGraphNodeRecord("cluster", "AKS Cluster") {
    StencilCatalog = VisioStencils.ContainersKubernetes
};
cluster.StencilQueries.Add("kubernetes");

var flow = new VisioGraphEdgeRecord("idp", "cluster") {
    Kind = VisioGraphConnectorKind.Control,
    Label = "tokens"
};
flow.ShapeData.Add("Protocol", "OIDC");

var runtime = new VisioGraphClusterRecord("runtime", "Runtime", new[] { "cluster" });
runtime.ShapeData.Add("Owner", "Platform");
runtime.HyperlinkAddress = "https://example.org/runtime-runbook";

VisioDocument.Create("inventory-graph.vsdx")
    .GraphDiagram("Imported Inventory", graph => graph
        .Legend()
        .Import(new[] { idp, cluster }, new[] { flow }, new[] { runtime })
        .Cluster("identity", "Identity", "idp", "cluster")
        .ZoneShapeData("identity", "Owner", "IAM"))
    .Save();

Use Legend() on generic graph diagrams when the generated page should explain which node kinds and connector kinds are present. The legend is derived from the actual graph, reserves header space during layout, and is marked as generated diagram adornment so polish and quality passes do not treat it as business content.

Quick sample (architecture diagram builder)

using OfficeIMO.Visio;
using OfficeIMO.Visio.Diagrams;

VisioDocument.Create("architecture.vsdx")
    .ArchitectureDiagram("Jenkins on Azure", diagram => diagram
        .Title()
        .Legend()
        .Theme(VisioStyleTheme.Technical())
        .Region("vnet", "Virtual Network", 1, 0, 4, 3)
        .Region("subnet", "Build Subnet", 1, 1, 4, 2)
        .Actor("users", "Users", 0, 1)
        .Gateway("public-ip", "Public IP", 1, 1)
        .Service("jenkins", "Jenkins Server", 2, 1)
        .Compute("agent", "Build Agent", 3, 1)
        .Database("data", "Data", 2, 2)
        .Storage("artifacts", "Artifacts", 4, 2)
        .Security("vault", "Key Vault", 2, 0)
        .DataFlow("users", "public-ip", "HTTPS")
        .DataFlow("public-ip", "jenkins", "route")
        .ControlFlow("jenkins", "agent", "scale")
        .Dependency("jenkins", "vault", "secrets")
        .Callout("jenkins", "scale-note", "Scale agents on demand", VisioSide.Right))
    .Save();

The architecture builder creates dependency-free cloud/infrastructure diagrams with semantic components, background regions, routed data/control/dependency connectors, labels, coordinate or side-placed callouts, and the reusable Technical theme.

Quick sample (network diagram builder)

using OfficeIMO.Visio;
using OfficeIMO.Visio.Diagrams;

VisioDocument.Create("network.vsdx")
    .NetworkDiagram("Branch Network", network => network
        .Title()
        .Theme(VisioStyleTheme.Technical())
        .Zone("perimeter", "Perimeter", 0, 0, 3, 1)
        .Zone("servers", "Server Zone", 3, 0, 3, 1)
        .Zone("clients", "Client LAN", 1, 2, 5, 1)
        .Internet("internet", "Internet", 0, 0)
        .Firewall("firewall", "Firewall", 1, 0)
        .Switch("core", "Core Switch", 2, 0)
        .Server("app", "App Server", 3, 0)
        .Database("db", "Database", 4, 0)
        .Workstation("pc1", "Finance PC", 1, 2)
        .Workstation("pc2", "Support PC", 2, 2)
        .Printer("printer", "Printer", 3, 2)
        .Ethernet("internet", "firewall", "WAN")
        .Trunk("firewall", "core", "uplink")
        .Trunk("core", "app", "10Gb")
        .Ethernet("app", "db")
        .Ethernet("core", "pc2")
        .Ethernet("pc2", "printer")
        .Callout("firewall", "edge-note", "Inspect and log inbound traffic", VisioSide.Top))
    .Save();

The network builder creates dependency-free network maps with zones, typed devices, routed Ethernet/trunk/wireless/management links, coordinate or side-placed semantic callouts, and optional legends.

Quick sample (network topology diagram builder)

using OfficeIMO.Visio;
using OfficeIMO.Visio.Diagrams;

VisioDocument.Create("network-topology.vsdx")
    .NetworkTopologyDiagram("Branch Topology", topology => topology
        .Title()
        .Root("internet", "Internet", VisioNetworkNodeKind.Internet)
        .Firewall("firewall", "Firewall")
        .Switch("core", "Core Switch")
        .Server("app", "App Server")
        .Database("db", "Database")
        .Workstation("finance", "Finance PC")
        .Workstation("support", "Support PC")
        .Printer("printer", "Printer")
        .Subnet("edge", "Edge", "internet", "firewall", "core")
        .Subnet("servers", "Server Zone", "app", "db")
        .Subnet("clients", "Client LAN", "finance", "support", "printer")
        .Ethernet("internet", "firewall", "WAN")
        .Trunk("firewall", "core", "uplink")
        .Trunk("core", "app", "10Gb")
        .Ethernet("app", "db")
        .Ethernet("core", "finance")
        .Ethernet("core", "support")
        .Ethernet("support", "printer")
        .Callout("firewall", "edge-note", "North-south inspection point", VisioSide.Top))
    .Save();

The topology builder is the graph-first network API: users describe devices and links, then OfficeIMO derives deterministic layers, grows the page when needed, adds subnet/background zones around selected devices, routes links, supports coordinate or side-placed semantic callouts, and keeps mesh/cycle links valid.

Quick sample (sequence diagram builder)

using OfficeIMO.Visio;
using OfficeIMO.Visio.Diagrams;

VisioDocument.Create("sequence.vsdx")
    .SequenceDiagram("Checkout Sequence", sequence => sequence
        .Title()
        .Theme(VisioStyleTheme.Fluent())
        .Actor("customer", "Customer")
        .Participant("web", "Web App")
        .Control("api", "Orders API")
        .Database("db", "Orders DB")
        .Call("customer", "web", "Checkout")
        .Call("web", "api", "POST /orders")
        .Async("api", "db", "Persist order")
        .Return("api", "web", "201 Created")
        .SelfMessage("web", "Render receipt"))
    .Save();

The sequence builder creates editable participants, lifelines, synchronous, asynchronous, return, and self-message connectors from semantic calls. It grows the page as needed, uses reusable style themes, and adds a native searchable sequence stencil catalog without depending on Visio templates at runtime.

Quick sample (swimlane diagram builder)

using OfficeIMO.Visio;
using OfficeIMO.Visio.Diagrams;

VisioDocument.Create("swimlane.vsdx")
    .SwimlaneDiagram("Order Fulfillment", swim => swim
        .Title()
        .Theme(VisioStyleTheme.Modern())
        .Lane("customer", "Customer")
        .Lane("sales", "Sales")
        .Lane("ops", "Operations")
        .Phase("request", "Request")
        .Phase("review", "Review")
        .Phase("approval", "Approval")
        .Phase("fulfill", "Fulfill")
        .Start("start", "Submit order", "customer", "request")
        .Step("qualify", "Qualify order", "sales", "review")
        .Decision("approved", "Approved?", "sales", "approval")
        .Step("revise", "Revise request", "customer", "approval")
        .Step("pick", "Pick items", "ops", "approval")
        .Data("invoice", "Create invoice", "sales", "fulfill")
        .End("ship", "Ship order", "ops", "fulfill")
        .Flow("start", "qualify", "handoff")
        .Flow("qualify", "approved")
        .Exception("approved", "revise", "no")
        .Handoff("approved", "pick", "yes")
        .Flow("pick", "invoice")
        .Flow("invoice", "ship")
        .Callout("approved", "approval-note", "Escalate exceptions before fulfillment", VisioSide.Right))
    .Save();

The swimlane builder creates editable role lanes, phase headers, semantic activities, labeled flows, dashed exception paths, deterministic routing, and automatic stacking when more than one activity lands in the same lane/phase cell. It supports coordinate or side-placed semantic callouts for risk and exception notes, and does not require Visio templates at runtime.

Loaded swimlane diagrams can be maintained with typed lane/phase/activity discovery and fluent moves:

using OfficeIMO.Visio;
using OfficeIMO.Visio.Fluent;

VisioDocument.Load("swimlane.vsdx")
    .AsFluent()
    .ExistingPage("Order Fulfillment", page => page
        .MoveSwimlaneActivity("pick", "sales", "review", options => {
            options.ActivityGap = 0.18;
            options.AvoidShapes = false;
        }))
    .End()
    .Save("swimlane-updated.vsdx");

Quick sample (org chart builder)

using OfficeIMO.Visio;
using OfficeIMO.Visio.Diagrams;

VisioDocument.Create("org-chart.vsdx")
    .OrgChartDiagram("Leadership", org => org
        .Title()
        .Theme(VisioStyleTheme.Modern())
        .Root("ceo", "Marta Nowak", "Chief Executive Officer")
        .Assistant("ea", "Eli Green", "Executive Assistant", "ceo")
        .Manager("cto", "Alex Chen", "Chief Technology Officer", "ceo")
        .Manager("coo", "Sam Rivera", "Chief Operating Officer", "ceo")
        .Manager("cfo", "Priya Shah", "Chief Financial Officer", "ceo")
        .TeamBand("engineering", "Engineering", "cto")
        .TeamBand("operations", "Operations", "coo")
        .Position("platform", "Nina Patel", "Platform Lead", "cto", "engineering")
        .Position("security", "Owen Brooks", "Security Lead", "cto", "engineering")
        .Vacancy("sre", "Open SRE Role", "coo", "operations")
        .External("advisor", "Taylor Reed", "Advisor", "cfo")
        .Callout("cto", "cto-note", "Owns platform and security roadmap", VisioSide.Right))
    .Save();

The org chart builder creates editable hierarchy cards, assistant placements, team bands, vacancies, external roles, routed reporting lines, and coordinate or side-placed semantic callouts from business relationships.

Reusable style themes

VisioStyleTheme gives diagrams and later editing passes a shared set of shape, connector, and readable text styles. The built-in presets are Modern, Office, Fluent, Technical, Enterprise, Cloud, Process, Minimal, Dark, DarkSafe, and Print. PremiumPresets() returns the professional set used for market-facing diagrams: enterprise, technical, cloud, process, print-safe, and dark-safe.

using OfficeIMO.Visio;
using OfficeIMO.Visio.Diagrams;

var process = VisioStyleTheme.Process();
var darkSafe = VisioStyleTheme.DarkSafe();
var premiumThemes = VisioStyleTheme.PremiumPresets();

var doc = VisioDocument.Create("styled.vsdx")
    .Flowchart("Styled Approval Flow", flow => flow
        .Theme(process)
        .Start("start", "Request received")
        .Step("review", "Review request")
        .Decision("approved", "Approved?")
        .End("done", "Done"));

var page = doc.Pages[0];
page.SelectByMaster("Decision").Style(process.Decision);
page.SelectConnectedConnectors(page.FindShapeById("approved")!)
    .Style(process.ControlConnector);
page.FitToContent(0.6, 0.45);
doc.Save();

VisioDocument.Create("dark-styled.vsdx")
    .BlockDiagram("Dark-Safe System Blocks", diagram => diagram
        .Theme(darkSafe)
        .Region("zone", "Processing Zone", 0, 0, 3, 1)
        .Block("input", "Input", 0, 0)
        .EmphasisBlock("processor", "Processor", 1, 0)
        .Block("output", "Output", 2, 0)
        .DataFlow("input", "processor")
        .ControlFlow("processor", "output", "control"))
    .Save();

Connector routing

Connectors can stay dynamic for Visio-managed rerouting, or they can be pinned to deterministic OfficeIMO-generated orthogonal routes and explicit waypoints. This is useful for readable flowcharts, architecture diagrams, and edited documents where a few important lines must avoid crossing the main content. OfficeIMO-authored explicit waypoint routes also load back into the connector model, so routed diagrams can be edited and saved again without losing the route semantics. Connectors can also be routed around unrelated top-level shapes with RouteOrthogonalAroundShapes, or page-wide with RouteConnectorsOrthogonalAroundShapes, when a generated path would otherwise cut through important content. The typed VisioConnectorRoutingOptions overloads can also treat containers, background surfaces such as zones or trust boundaries, and generated adornments as obstacles when those surfaces should visibly reserve routing space. Containers and background surfaces that contain the connector source or target are ignored, so a connector can still start or end inside its own zone. The same options can prefer lanes that reduce connector-to-connector crossings when reference connectors are supplied; page-wide routing supplies the page connector set automatically and can run deterministic page-level optimization passes so the most conflicted connectors are considered first on later sweeps. Dense pages can also use multi-waypoint dogleg candidates when a simple three-segment orthogonal route still crosses important content. Pages can also set native Visio routing defaults for connectors that do not carry local routing or line-jump settings, plus placement and layout-grid policy used by Visio's Re-Layout Page commands.

using System.Linq;
using OfficeIMO.Visio;
using OfficeIMO.Visio.Stencils;

var doc = VisioDocument.Create("routes.vsdx");
var page = doc.AddPage("Routes");
page.PlacementStyle = VisioPlacementStyle.HierarchyLeftToRightMiddle;
page.PlacementDepth = VisioPlacementDepth.Medium;
page.PlacementFlip = VisioPlacementFlip.Horizontal | VisioPlacementFlip.Rotate90;
page.MoveShapesAwayOnDrop = true;
page.ResizePageToFitLayout = true;
page.EnableLayoutGrid = true;
page.SetLayoutGridSizing(1.2, 0.45);
page.ConnectorRouteStyle = VisioPageRouteStyle.FlowchartTopToBottom;
page.ConnectorRouteAppearance = VisioLineRouteExtension.Straight;
page.LineJumpStyle = VisioLineJumpStyle.Gap;
page.LineJumpCode = VisioLineJumpCode.DisplayOrder;
page.HorizontalLineJumpDirection = VisioHorizontalLineJumpDirection.Up;
page.VerticalLineJumpDirection = VisioVerticalLineJumpDirection.Right;
page.SetConnectorSpacing(0.25, 0.45);

var source = page.AddStencilShape(VisioStencils.Flowchart.Get("process"),
    "source", 2, 5, "Source");
var target = page.AddStencilShape(VisioStencils.Flowchart.Get("process"),
    "target", 7, 3, "Target");
target.PlacementStyle = VisioPlacementStyle.HierarchyLeftToRightMiddle;
target.PlacementFlip = VisioPlacementFlip.Horizontal | VisioPlacementFlip.Rotate90;
target.PlowCode = VisioShapePlowCode.Always;
target.AllowHorizontalConnectorRoutingThrough = false;
target.AllowVerticalConnectorRoutingThrough = false;

VisioConnector route = page.AddConnector(source, target, ConnectorKind.Dynamic,
        VisioSide.Right, VisioSide.Left);
route.RouteStyle = VisioPageRouteStyle.FlowchartLeftToRight;
route.RouteAppearance = VisioLineRouteExtension.Curved;
route.LineJumpStyle = VisioLineJumpStyle.Square;
route.LineJumpCode = VisioConnectorLineJumpCode.Always;
route.HorizontalJumpDirection = VisioHorizontalLineJumpDirection.Up;
route.VerticalJumpDirection = VisioVerticalLineJumpDirection.Right;
route.RerouteBehavior = VisioConnectorRerouteBehavior.OnCrossover;
route
    .RouteOrthogonal(VisioConnectorRouteStyle.HorizontalThenVertical)
    .PlaceLabel(0.65, offsetY: 0.18)
    .ApplyStyle(VisioStyleTheme.Modern().Connector);

page.SelectConnectedConnectors(source)
    .RouteThrough(VisioConnectorWaypoint.At(4.5, 5),
        VisioConnectorWaypoint.At(4.5, 3))
    .Label("handoff")
    .LabelPosition(0.6, offsetX: 0.15);

page.RouteConnectorsOrthogonalAroundShapes(new VisioConnectorRoutingOptions {
    Padding = 0.12,
    MaxLanes = 16,
    IncludeContainers = true,
    IncludeBackgroundSurfaces = true,
    AvoidConnectorCrossings = true
});

doc.Save();

Timeline roadmaps

The timeline builder creates date-scaled roadmap diagrams with milestone semantics, above/below placement, stacked labels, and span lanes. It is useful for release plans, migration schedules, project phases, and executive roadmap views where the author should provide dates, not hand-place every marker.

using OfficeIMO.Visio;
using OfficeIMO.Visio.Diagrams;

VisioDocument.Create("roadmap.vsdx")
    .TimelineDiagram("Product Roadmap", timeline => timeline
        .Title()
        .Theme(VisioStyleTheme.Modern())
        .Range(new DateTime(2026, 1, 1), new DateTime(2026, 6, 30))
        .Span("discovery", new DateTime(2026, 1, 8), new DateTime(2026, 2, 20), "Discovery")
        .Span("build", new DateTime(2026, 2, 21), new DateTime(2026, 5, 15), "Build", lane: 1)
        .Release("preview", new DateTime(2026, 5, 20), "Public preview", VisioTimelinePlacement.Below)
        .Milestone("ga", new DateTime(2026, 6, 25), "GA")
        .Callout("build", "build-note", "Implementation runway", VisioSide.Top))
    .Save();

Timeline callouts can target either milestone IDs or span IDs, so roadmap notes stay attached to the dated item they explain. They can be placed by coordinates or relative to the target side.

Package validation proves the .vsdx structure is sound. Visual quality checks catch common diagram problems before a human opens Visio: shapes outside the page, overlapping shapes, routed connectors crossing unrelated shapes, and connector labels placed off-page, over unrelated shapes, or on top of each other.

using OfficeIMO.Visio;

var results = VisioGallery.Create("gallery");
foreach (var result in results) {
    if (!result.IsClean) {
        foreach (var issue in result.QualityIssues) {
            Console.WriteLine(issue);
        }
    }
}

var proofOptions = new VisioGalleryOptions {
    ValidateWithVisioDesktop = true,
    DesktopValidationOptions = VisioDesktopValidationOptions.RoundTripWithSvg()
};
var proofResults = VisioGallery.Create("gallery-proof", proofOptions);

// Gallery coverage includes identity/authentication, privileged access review,
// Kubernetes/service mesh, application dependency, data-platform lineage,
// hybrid network operations, process governance, incident sequence, and network segmentation
// scenarios with validation metadata.

var issues = doc.AnalyzeVisualQuality(new VisioDiagramQualityOptions {
    RequireConnectorLabels = false,
    CheckConnectorLabelOverlaps = true,
    CheckConnectorLabelShapeOverlaps = true
});
var report = doc.GetVisualQualityReport();
doc.EnsureVisualQuality(minimumSeverity: VisioDiagramQualityIssueSeverity.Warning);

page.ResolveConnectorLabelOverlaps();
doc.PolishDiagrams();

zoneShape.MarkAsBackgroundSurface();
captionShape.MarkAsGeneratedDiagramAdornment();

The reusable gallery includes data-driven CI/CD inventory, identity authentication, privileged-access review, Kubernetes service-mesh, application-dependency, data-platform lineage, hybrid network operations, and process governance review graphs built from VisioGraphNodeRecord, VisioGraphEdgeRecord, and VisioGraphClusterRecord records. They use first-party stencil catalogs, generated clusters, Shape Data, hyperlinks, and automatic graph legends, so the gallery exercises real inventory-to-diagram workflows rather than only coordinate-authored examples.

Showcase runs can also write reviewable proof metadata for generated packages, previews, inspection snapshots, stencil-profile summaries, and visual-quality summaries, including top-level proof and evidence totals, structural shape/connector counts, Shape Data key counts, native/desktop preview evidence, connector Shape Data key counts, semantic-kind counts, stencil-backed/basic-geometry mix, connection-point coverage, parsed visual-quality quality.* counts, clean visual-quality totals, and stencil provenance. VisioShowcaseSummary emits Markdown, JSON, and browsable HTML artifacts, and EnsureArtifactsValid(...) recomputes file sizes and SHA-256 hashes so stale or modified proof files fail before they are published. The HTML gallery includes a review index with stable diagram deep links, headline proof metrics, compact SHA-256 fingerprints with the full hash available on hover, visual-quality proof links parsed from .visual-quality.txt artifacts, and stencil catalog proof summaries parsed from .stencil-profile.txt artifacts. It also includes proofTotals, evidenceTotals, shape/connector/Shape Data rollups, stencil-backed/basic-geometry mix, connection-point coverage, native/desktop preview completeness, clean visual-quality totals, complete structural proof, complete review proof, and a stencil coverage table so reviewers can see both overall generated coverage and which diagrams exercise each catalog. The JSON output includes schemaVersion, artifactCount, proofTotals, evidenceTotals, stencilCatalogCoverage, and per-diagram proofSummary / evidence fields for downstream CI/review tooling:

var summary = VisioShowcaseSummary.Create(
    "gallery-proof",
    Directory.EnumerateFiles("gallery-proof", "*.vsdx"),
    Directory.EnumerateFiles(Path.Combine("gallery-proof", "Native Preview"), "*.*"),
    proofFiles: Directory.EnumerateFiles(Path.Combine("gallery-proof", "Structural Proof"), "*.txt"));

summary.EnsureArtifactsValid(requirePreviewsPerDiagram: true, requireProofsPerDiagram: true);
summary.SaveArtifacts();

EnsureVisualQuality(...) throws VisioDiagramQualityException with the blocking issues, which makes it practical to use generated diagrams in tests or CI without writing custom issue-loop code.

Inspection snapshots and structural diffs

Inspection snapshots give tests, review tools, and stencil/profile tooling a deterministic view of a generated or loaded diagram without requiring Visio desktop automation. The snapshot includes document metadata, pages, masters, shapes, connectors, Shape Data, User cells, semantic OfficeIMO tags, layers, waypoints, and stable text output.

using OfficeIMO.Visio;

VisioInspectionSnapshot before = doc.CreateInspectionSnapshot();
string snapshotText = before.ToText();

// ... change, reload, or regenerate the diagram ...

VisioInspectionSnapshot after = doc.CreateInspectionSnapshot();
VisioInspectionDiff diff = before.Diff(after);
if (diff.HasDifferences) {
    Console.WriteLine(diff.ToText());
}

VisioStencilProfile profile = after.CreateStencilProfile();
Console.WriteLine(profile.ToText());

Use inspection snapshots alongside package validation and optional PNG/SVG preview baselines: the snapshot explains what changed structurally, while the stencil profile reports how much of the diagram is generated-master, package-backed, or basic-geometry driven. Stencil placement stamps catalog, category, stencil id, tags, and source package path into shape/master metadata, so package-backed and generated-stencil profiles survive reloads and can audit saved files as well as in-memory documents. The rendered preview proves how the diagram looks. The premium baseline lane also stores approved inspection and stencil-profile text snapshots for each rendered diagram. PNG drift writes expected, actual, and .diff.png artifacts with changed-pixel counts, while inspection/profile drift writes expected, actual, and .diff.txt artifacts so reviewers can distinguish renderer-only churn from structural or stencil usage changes. SVG previews use canonicalized text comparison to account for Visio's generated CSS class names. A no-Visio premium baseline lane also compares first-party native SVG and PNG output for representative gallery diagrams, so renderer drift can be caught on machines that do not have Microsoft Visio installed.

Headless SVG and PNG export

OfficeIMO Visio can render generated pages directly to SVG or PNG without Microsoft Visio desktop automation. These headless paths are intended for CI artifacts, documentation previews, web galleries, and quick inspection of generated diagrams:

using OfficeIMO.Visio;

VisioDocument document = VisioDocument.Create("pipeline.vsdx");
VisioPage page = document.AddPage("Pipeline").Size(8, 4);
VisioShape build = page.AddProcess(1.5, 2, 1.4, 0.7, "Build");
VisioShape ship = page.AddProcess(5.5, 2, 1.4, 0.7, "Ship");
page.AddConnector(build, ship, ConnectorKind.RightAngle, VisioSide.Right, VisioSide.Left).EndArrow = EndArrow.Arrow;

string svg = document.ToSvg();
document.SaveAsSvg("pipeline.svg", new VisioSvgSaveOptions {
    PixelsPerInch = 96,
    BackgroundColor = null
});

byte[] png = document.ToPng(new VisioPngSaveOptions {
    PixelsPerInch = 144,
    Supersampling = 3,
    FontFilePath = "path/to/font.ttf"
});
document.SaveAsPng("pipeline.png");

The native SVG renderer covers OfficeIMO-authored shapes, connectors, labels, bounded text wrapping/scaling with long-word breaks, styled underline/italic attributes, text rotation, rotated styled text-block backgrounds with Visio transparency, connector-label backgrounds, render-time connector-label overlap avoidance including endpoint-shape, dense-label clearance, and connector-line crossing avoidance, metadata-driven first-party stencil pictograms with authored shape rotation for straight and curved glyphs, text styles, common flowchart geometry, simple preserved Visio MoveTo/LineTo/PolylineTo and relative geometry-row outlines with intra-section subpath breaks and unclosed NoFill open paths, deleted Geometry/SplineKnot rows, simple preserved Width/Height/LocPinX/LocPinY/PinX/PinY/Angle/MIN/MAX/ABS/SQRT/PI/SIN/COS/TAN/ATAN/ATAN2/RAD/DEG/INT/POW/^/ROUND/AND/OR/NOT/GUARD/IF formulas including POLYLINE arguments and percentage plus angle/length unit-suffixed numeric literals, preserved NoFill/NoLine/NoShow geometry flags, scaled master/master-shape preserved outlines, preserved Ellipse and open clipped InfiniteLine rows, flattened preserved ArcTo/EllipticalArcTo, RelEllipticalArcTo, CubBezTo/QuadBezTo, and RelCubBezTo/RelQuadBezTo, SplineStart/SplineKnot, plus NURBSTo formula outlines with Visio compact knot-vector expansion, transparency, dashed strokes, semantic database/storage cylinder bodies, semantic flowchart start/end terminator capsules, semantic document stencil wavy bottoms, built-in chevron polygons, delay D-shapes, and manual input slanted quadrilaterals, color/opacity-matching inline arrowheads, and rotated browser-renderable package-backed preview/icon payloads, including content-sniffed generic media relationships, when package masters expose embedded PNG/JPG/GIF/SVG media. The native PNG renderer uses the same OfficeIMO-authored geometry surface and writes PNG bytes directly, including basic anti-aliased shapes with authored ellipse rotation, semantic database/storage cylinder bodies, semantic flowchart start/end terminator capsules, semantic document stencil wavy bottoms, built-in chevron polygons, delay D-shapes, and manual input slanted quadrilaterals, dashed shape/connector strokes, connectors, arrows, wrapped labels and long unspaced shape text, styled text-block backgrounds, metadata-driven first-party stencil pictograms with authored shape rotation for straight and curved glyphs, embedded package PNG preview/icon payloads including content-sniffed generic media relationships, truecolor, indexed-palette, grayscale, and grayscale-alpha PNGs, including packed 1/2/4-bit indexed/grayscale icons and 16-bit channel payloads downsampled into the native preview buffer, honoring palette and truecolor tRNS transparency, with aspect-preserving placement and shape rotation, managed TrueType/OpenType outline text from a configured FontFilePath/FontCollectionIndex/FontFaceName or from managed default font-file discovery where available, styled text underlines/italics, TextAngle rotation, rotated styled text-block backgrounds, a small stroke-font fallback, and optional transparent backgrounds. The PNG writer uses managed DEFLATE compression, shares the same render-time connector-label overlap avoidance including endpoint-shape, dense-label clearance, and connector-line crossing avoidance, and does not call operating-system graphics or font APIs. Set ResolveConnectorLabelOverlaps = false when a preview must preserve authored label pins exactly, or RenderStencilArtwork = false when the native SVG/PNG preview should show only the authored shape geometry. Desktop Visio export remains the higher-fidelity proof path for complex Visio-authored content, non-PNG package media in native PNG, native master/vector geometry, and final visual parity. The no-Visio native baseline lane now covers all eight premium gallery diagrams so renderer drift is gated without requiring Microsoft Visio.

Native stencil catalogs

Built-in stencil catalogs give you reusable, searchable shape definitions while still generating masters from OfficeIMO code. They are not .vssx or .vsdx runtime dependencies. Use Get(...) for exact known shapes and Search(...) or InCategory(...) when you want user-friendly discovery by id, name, master, category, keyword, alias, or tag. Each stencil also carries IconNameU preview metadata for palette and picker UIs.

The built-in catalog set covers basic, flowchart, block-diagram, architecture, network, infrastructure, cloud, security/identity, containers/Kubernetes, data/platform, collaboration/business process, sequence, swimlane, org-chart, and timeline domains. These first-party definitions are dependency-free and preserve searchable domain intent in inspection snapshots and stencil profiles.

using OfficeIMO.Visio;
using OfficeIMO.Visio.Stencils;

var doc = VisioDocument.Create("stencils.vsdx");
var page = doc.AddPage("Catalog");

var process = page.AddStencilShape(VisioStencils.Flowchart.Get("process"),
    "receive", 2, 4, "Receive request");
var decision = page.AddStencilShape(VisioStencils.Flowchart, "branch",
    "approved", 5, 4, "Approved?");
var switchShape = page.AddStencilShape("net.switch", "switch", 8, 6, "Switch");
var dataStore = VisioStencils.All.Search("data-store").First();
var identityProvider = VisioStencils.SecurityIdentity.Get("idp");
var kubernetesCluster = VisioStencils.ContainersKubernetes.Get("kubernetes");
var dataPipeline = VisioStencils.DataPlatform.Get("etl");
var networkShapes = VisioStencils.All.InCategory("Network");
var custom = VisioStencilCatalog.Create("Custom Infrastructure", catalog => catalog
    .Add("custom.cache", "Cache", "Process", "Infrastructure", 1.8, 0.9, "redis")
    .AddWithMetadata("custom.archive", "Object Archive", "Data", "Infrastructure",
        1.8, 0.9,
        keywords: new[] { "blob" },
        aliases: new[] { "object-store" },
        tags: new[] { "cloud", "storage" },
        iconNameU: "Data"));
var cache = page.AddStencilShape(custom, "redis", "cache", 8, 4, "Cache");
var packageCatalog = VisioStencilPackageCatalog.Load("network.vssx",
    new VisioStencilPackageLoadOptions {
        Category = "Network",
        MasterNames = new[] { "Server", "rId4", "database-cylinder" },
        LearnMasterDimensions = true,
        IncludeUnsupportedMasters = false
    });
custom.Save("infrastructure.officeimo-visio-stencils.xml");
var reusable = VisioStencilCatalog.Load("infrastructure.officeimo-visio-stencils.xml");

page.AddConnector(process, decision, ConnectorKind.Dynamic,
    VisioSide.Right, VisioSide.Left);
doc.Save();

VisioStencilPackageCatalog.Load(...) reads master metadata from .vsdx, .vssx, .vstx, and the macro-enabled package variants. It does not use those files as runtime templates. The MasterNames filter can target the universal name, visible name, relationship id, numeric id, or normalized slug discovered in the package. Package catalogs can learn native master dimensions, preview/icon image relationship metadata, and native connection points; when a package-backed stencil is placed, those connection points are scaled onto the page shape so connectors, inspection snapshots, and stencil profiles can see the real stencil attachment profile.

When you want real external artwork, load the package catalog with IncludeUnsupportedMasters = true and place shapes from that catalog. Package catalog shapes retain their SourcePackagePath, so AddStencilShape(...) auto-imports the required raw master XML, relationships, media, colors, styles, fonts, and theme into the generated .vsdx:

using OfficeIMO.Visio;
using OfficeIMO.Visio.Stencils;

var catalog = VisioStencilPackageCatalog.Load("Azure.vssx",
    new VisioStencilPackageLoadOptions {
        IncludeUnsupportedMasters = true
    });

var doc = VisioDocument.Create("external-stencils.vsdx");
var page = doc.AddPage("Architecture", 14, 8.5);

var api = page.AddStencilShape(catalog.Get("API Management"), "api", 2, 5);
var queue = page.AddStencilShape(catalog.Search("Service Bus").First(), "queue", 5, 5);

page.AddConnector(api, queue, ConnectorKind.Straight, VisioSide.Right, VisioSide.Left);
doc.Save();

Use LoadMany(...) or LoadDirectory(...) to compose a palette from many packs. That is the preferred model for repository-style stencil packs, such as the Microsoft Integration and Azure community pack, where the useful masters are spread across multiple .vssx files:

var packages = VisioStencilPackageCatalog.EnumeratePackageFiles(
    @"C:\StencilPacks\Microsoft-Integration-and-Azure-Stencils-Pack-for-Visio",
    recursive: true);
var integration = VisioStencilPackageCatalog.LoadMany(packages,
    new VisioStencilPackageLoadOptions {
        CatalogName = "Microsoft Integration and Azure",
        IncludeUnsupportedMasters = true
    });

var apim = integration.Search("API Management").First();
var serviceBus = integration.Search("Service Bus").First();

To inspect a pack before building a diagram, render a catalog contact sheet:

var doc = VisioDocument.Create("stencil-gallery.vsdx");
var page = doc.AddPage("Gallery", 11, 8.5);
page.AddStencilGallery(integration, new VisioStencilGalleryOptions {
    Title = "Microsoft Integration and Azure",
    Columns = 4,
    MaxShapes = 24,
    IncludeStencilMetadataShapeData = true
});
doc.Save();

For a complete catalog review artifact, create a paginated gallery document. It adds an overview page, splits large catalogs by category, and stamps each preview shape with Shape Data rows such as stencil id, category, catalog, master, keywords, aliases, tags, default size, source package, preview image, and connection-point counts where available:

var gallery = VisioStencilGalleryDocument.Create("stencil-gallery.vsdx",
    integration,
    new VisioStencilGalleryDocumentOptions {
        Title = "Microsoft Integration and Azure review",
        Columns = 4,
        ShapesPerPage = 24,
        IncludeStencilMetadataShapeData = true
    });
gallery.Save();

For package-backed masters that carry embedded preview/icon payloads, export a reviewable HTML inventory and the raw image payloads:

var previewGallery = VisioStencilPackageCatalog.CreatePreviewGallery(
    @"C:\StencilPacks\Azure.vssx",
    @"C:\Temp\AzureStencilPreview",
    new VisioStencilPackageLoadOptions {
        IncludeUnsupportedMasters = true
    },
    new VisioStencilPreviewGalleryOptions {
        Title = "Azure stencil preview review"
    });

Console.WriteLine(previewGallery.IndexPath);

Browser-friendly payloads such as PNG, JPG, SVG, GIF, BMP, and WebP render inline in the generated index. Other native Visio/Office payloads such as EMF are still extracted and listed with their content type, relationship target, byte length, and saved file link for external review tools. Browser-renderable payloads also receive deterministic SVG thumbnail wrappers in the thumbnails directory by default, so catalog review output has stable visual artifacts that can be archived or diffed beside the raw embedded media.

DiscoverInstalledVisioPackages() finds the local Microsoft Visio .vssx and .vstx content folders without automating Visio, letting you build diagrams from installed Visio stencils while keeping OfficeIMO itself dependency-free:

var installed = VisioStencilPackageCatalog.DiscoverInstalledVisioPackages()
    .Where(path => Path.GetFileName(path).StartsWith("AZURE", StringComparison.OrdinalIgnoreCase));
var azure = VisioStencilPackageCatalog.LoadMany(installed,
    new VisioStencilPackageLoadOptions {
        CatalogName = "Installed Azure Stencils",
        IncludeUnsupportedMasters = true
    });

VisioStencilCatalog.Save(...) and VisioStencilCatalog.Load(...) persist OfficeIMO-native catalog metadata as a small XML manifest. This is useful for reusable first-party or application-specific palettes without requiring Visio stencil packages at runtime. The manifest preserves source package paths, preview metadata, learned default sizes, and learned source connection points.

Query and selection editing

Query helpers let you edit diagrams by meaning instead of by page indexes. They work with generated and loaded shapes, including nested group children.

using OfficeIMO.Visio;
using OfficeIMO.Visio.Stencils;
using Color = OfficeIMO.Drawing.OfficeColor;

var doc = VisioDocument.Create("editing.vsdx");
var page = doc.AddPage("Ownership");

var intake = page.AddStencilShape(VisioStencils.Flowchart.Get("process"),
    "intake", 2, 5, "Receive");
var review = page.AddStencilShape(VisioStencils.Flowchart.Get("process"),
    "review", 5, 5, "Review");
var decision = page.AddStencilShape(VisioStencils.Flowchart.Get("decision"),
    "approved", 8, 5, "Approved?");

page.AddConnector(intake, review, ConnectorKind.Dynamic, VisioSide.Right, VisioSide.Left);
page.AddConnector(review, decision, ConnectorKind.Dynamic, VisioSide.Right, VisioSide.Left);

intake.Data["Owner"] = "Ops";
review.Data["Owner"] = "Ops";

page.SelectWithData("Owner", "Ops")
    .Fill(Color.LightBlue)
    .Stroke(Color.DodgerBlue, 0.02)
    .Duplicate(new VisioShapeDuplicationOptions {
        OffsetX = 1.5,
        OffsetY = -0.75,
        IdSuffix = "-copy",
        ConnectorIdSuffix = "-copy"
    });

page.SelectOutgoingConnectors(review)
    .LineColor(Color.DodgerBlue)
    .EndArrow(EndArrow.Triangle);

page.SelectContainedIn(page.ShapesWithData("Owner", "Ops").First().GetShapeBounds())
    .ShapeData("ReviewScope", "Operations");

page.SelectConnectedComponent(review)
    .ShapeData("Component", "Approval");

page.SelectWithShapeData("Risk", value => int.TryParse(value, out int risk) && risk >= 4)
    .Stroke(Color.Red, 0.025);

doc.Save();

Selection duplication remaps copied shape identifiers and duplicates only the connectors whose endpoints are both inside the copied selection. Shape styling, layers, hyperlinks, User cells, typed Shape Data, protection, layout hints, and connector routing metadata move with the copy. VisioShapeDuplicationOptions lets callers control offsets, connector copying, semantic ID suffixes, and advanced shape/connector ID factories without adding serializer or reflection dependencies.

Fluent page editing exposes the same copy workflow with friendly default -copy identifiers, so duplicated shapes remain addressable in the same chain:

document.AsFluent()
    .ExistingPage("Operations", page => page
        .DuplicateShapes(new[] { "api", "database" }, copies => copies
            .ShapeData("Copied", "Yes"))
        .Shape("api-copy", shape => shape.Text("API copy"))
        .Connect("api-copy", "database-copy", VisioSide.Right, VisioSide.Left,
            connector => connector.Label("copied route")));

The same query surface supports editing loaded diagrams by geometry and graph structure: ShapesIntersecting(...), ShapesContainedIn(...), ShapesWithShapeData(...), ConnectedComponent(...), and PathBetween(...) can find contained shapes, overlapping annotations, connected islands, and shortest connected paths before applying bulk style or metadata edits.

Whole pages can be duplicated as well. The copy receives fresh shape and connector IDs while keeping page settings, layers, background-page linkage, shape metadata, internal connectors, labels, and explicit routes:

VisioPage reviewPage = page.Duplicate("Review copy");

VisioPage independentReviewPage = page.Duplicate(new VisioPageDuplicationOptions {
    Name = "Review copy",
    DuplicateBackgroundPage = true,
    BackgroundPageName = "Review background copy"
});

Existing shapes can also be retargeted to a different generated master without losing their editing metadata or connector endpoints:

page.SelectByMaster("Process")
    .ReplaceMaster(VisioStencils.Flowchart.Get("decision"), resizeToMaster: true);

page.ReplaceMaster(archiveShape, "Data");

Layers

Pages support Visio-native layers. Shapes and connectors can belong to one or more layers; OfficeIMO writes the page Layer section and the shape LayerMember cells directly, so the document opens in Visio with editable layer membership.

using OfficeIMO.Visio;
using OfficeIMO.Visio.Stencils;
using Color = OfficeIMO.Drawing.OfficeColor;

var doc = VisioDocument.Create("layers.vsdx");
var page = doc.AddPage("Layered");
page.AddLayer("Infrastructure");
page.AddLayer("Annotations").Print = false;

var server = page.AddStencilShape(VisioStencils.Network.Get("server"),
    "server", 2, 5, "Server");
var note = page.AddStencilShape(VisioStencils.BasicShapes.Get("rectangle"),
    "note", 5, 5, "Internal note");

page.AddToLayer("Infrastructure", server)
    .AddToLayer("Annotations", note);

page.SelectLayer("Infrastructure")
    .Stroke(Color.DodgerBlue, 0.02);

doc.Save();

Shapes and connectors can carry native Visio hyperlink rows. OfficeIMO writes the ShapeSheet Hyperlink section directly and loads it back into typed VisioHyperlink objects, while preserving unknown hyperlink cells from files created elsewhere.

using OfficeIMO.Visio;
using Color = OfficeIMO.Drawing.OfficeColor;

var doc = VisioDocument.Create("hyperlinks.vsdx");
var page = doc.AddPage("Linked");

var portal = page.AddRectangle(2, 5, 2, 1, "Portal");
var api = page.AddRectangle(5, 5, 2, 1, "API");

portal.AddHyperlink("https://github.com/EvotecIT/OfficeIMO", "Repository");
var connector = page.AddConnector(portal, api, ConnectorKind.Dynamic, VisioSide.Right, VisioSide.Left);
connector.AddHyperlink("https://example.org/openapi.json", "API contract");

page.SelectWithHyperlinks()
    .Fill(Color.LightYellow)
    .Stroke(Color.DodgerBlue, 0.02);
page.SelectConnectorsWithHyperlinks()
    .EndArrow(EndArrow.Triangle);

doc.Save();

Shape Data

Shapes support typed Visio Shape Data rows in the ShapeSheet Prop section. The simple Data dictionary still works, while SetShapeData lets you keep labels, prompts, types, formats, sort keys, and other metadata visible in Visio's Shape Data window.

using OfficeIMO.Visio;
using Color = OfficeIMO.Drawing.OfficeColor;

var doc = VisioDocument.Create("shape-data.vsdx");
var page = doc.AddPage("Shape Data");

var api = page.AddRectangle(2.5, 4, 2.2, 1, "API");
api.SetShapeData("Owner", "Platform", "Owner",
    VisioShapeDataType.String, "Owning support team");
api.SetShapeData("MonthlyCost", "1250", "Monthly cost",
    VisioShapeDataType.Currency, "Estimated monthly cost", "$#,##0");

var database = page.AddRectangle(6, 4, 2.2, 1, "Database");
database.SetShapeData("Owner", "Data", "Owner",
    VisioShapeDataType.String, "Owning support team");

page.SelectWithShapeData("Owner", "Platform")
    .Fill(Color.LightBlue)
    .ShapeData("Reviewed", "Yes", "Reviewed",
        VisioShapeDataType.Boolean, "Architecture review complete");

doc.Save();

For repeatable metadata across generated or loaded diagrams, define a reusable schema and apply it to shapes, shape selections, connectors, or connector selections. Existing values are preserved by default while labels, prompts, types, list formats, sort keys, visibility, and verification settings are standardized.

var schema = VisioShapeDataSchema.Create()
    .Field("Owner", "Owner", VisioShapeDataType.String,
        defaultValue: "Unassigned", prompt: "Owning team",
        sortKey: "010", required: true)
    .Field("Risk", "Risk", VisioShapeDataType.FixedList,
        defaultValue: "Medium", prompt: "Operational risk",
        sortKey: "020", required: true, verify: true,
        allowedValues: new[] { "Low", "Medium", "High" })
    .Field("MonthlyCost", "Monthly cost", VisioShapeDataType.Currency,
        defaultValue: "0", prompt: "Estimated monthly run cost",
        format: "$#,##0", sortKey: "030");

schema.ApplyTo(api);
page.SelectWithShapeData("Owner", value => string.IsNullOrWhiteSpace(value))
    .ShapeData(schema);

var issues = schema.Validate(api);

Shape Data can also be surfaced as visible data graphics. Generated badges and bars are stored as diagram adornments tied back to the target shape and source field, so routing, quality checks, inspection snapshots, and stencil profiles can distinguish them from business shapes.

api.SetShapeData("Status", "Healthy", "Status",
    VisioShapeDataType.FixedList, format: "Healthy;Warning;Critical");
api.SetShapeData("Slo", "72", "SLO", VisioShapeDataType.Number);

var dataGraphic = VisioDataGraphic.Create()
    .Badge("Status")
    .Bar("Slo", maximumValue: 100, label: "SLO");

page.SelectWithShapeData("Status", value => !string.IsNullOrWhiteSpace(value))
    .AddDataGraphics(dataGraphic);

Page settings

Pages expose common print and page-management cells without requiring raw ShapeSheet XML: margins, print orientation, page replacement/duplication locks, drawing size behavior, automatic page resizing, shape splitting, and whether a page is visible in Visio page lists.

using OfficeIMO.Visio;

var doc = VisioDocument.Create("page-settings.vsdx");
var page = doc.AddPage("Print ready", 11, 8.5);
page.SetMargins(0.4, 0.5, 0.6, 0.7);
page.PrintOrientation = VisioPagePrintOrientation.Landscape;
page.PageLockReplace = true;
page.DrawingSizeType = VisioDrawingSizeType.Custom;
page.AutoResizeDrawing = false;
page.AllowShapeSplitting = false;
page.UiVisibility = VisioPageUiVisibility.Normal;
page.AddRectangle(5.5, 4.25, 2.4, 1, "Print-ready page");

doc.Save();

Background pages

Reusable Visio background pages can hold title bands, watermarks, legends, page frames, or locked diagram furniture once, then foreground pages can reference them with native BackPage links.

using OfficeIMO.Visio;
using Color = OfficeIMO.Drawing.OfficeColor;

var doc = VisioDocument.Create("background-pages.vsdx");

var background = doc.AddBackgroundPage("Brand background", 11, 8.5);
background.AddRectangle(5.5, 8.05, 10.5, 0.45, "OfficeIMO generated")
    .Protect(p => p.Size().Position().Text().Selection())
    .FillColor = Color.LightBlue;

var architecture = doc.AddPage("Architecture", 11, 8.5);
architecture.SetBackgroundPage(background);
architecture.AddRectangle(3.5, 4.8, 2.2, 1, "API");
architecture.AddRectangle(7.5, 4.8, 2.2, 1, "Worker");

var operations = doc.AddPage("Operations", 11, 8.5);
operations.SetBackgroundPage(background);
operations.AddRectangle(5.5, 4.8, 2.2, 1, "Runbook");

doc.Save();

Shape protection

Shapes and connectors expose native Visio Lock* ShapeSheet cells for diagrams that should open cleanly in Visio but protect generated scaffolding and routed connectors from accidental edits. Protection round-trips from existing .vsdx files and works with selections.

using OfficeIMO.Visio;
using Color = OfficeIMO.Drawing.OfficeColor;

var doc = VisioDocument.Create("protected-diagram.vsdx");
var page = doc.AddPage("Protected Diagram");

var background = page.AddRectangle(4.25, 3, 7.5, 4.8, "Generated zone");
background.FillColor = Color.LightCyan;
background.Protect(p => p.Size().Position().Selection().Formatting());

var api = page.AddRectangle(3, 3.8, 2, 1, "API");
api.SetShapeData("Owner", "Platform");
var db = page.AddRectangle(6, 3.8, 2, 1, "Database");
var link = page.AddConnector(api, db, ConnectorKind.Dynamic,
    VisioSide.Right, VisioSide.Left);
link.Label = "read";
link.Protect(p => p.Endpoints().Text().Deletion());

page.SelectWithData("Owner", "Platform")
    .Protect(p => p.Text().Deletion())
    .Fill(Color.LightYellow);

page.SelectConnectorsWithProtection()
    .Protect(p => p.Formatting());

doc.Save();

Containers and User cells

Pages can create Visio-native containers around existing shapes. OfficeIMO writes the ShapeSheet User section and Relationships cells directly, so containers open as semantic Visio structures rather than as decorative boxes. Generic User cells are also available for custom ShapeSheet metadata.

using OfficeIMO.Visio;
using OfficeIMO.Visio.Stencils;
using Color = OfficeIMO.Drawing.OfficeColor;

var doc = VisioDocument.Create("containers.vsdx");
var page = doc.AddPage("Application");

var api = page.AddStencilShape(VisioStencils.Network.Get("server"),
    "api", 3, 5.5, "API");
var worker = page.AddStencilShape(VisioStencils.Network.Get("server"),
    "worker", 6, 5.5, "Worker");

var tier = page.AddContainer("app-tier", "Application tier",
    new[] { api, worker },
    new VisioContainerOptions {
        Margin = 0.35,
        FillColor = Color.LightCyan,
        LineColor = Color.DodgerBlue
    });

tier.SetUserCell("OfficeIMO.Role", "Tier", "STR", prompt: "Semantic role");

page.SelectContainers()
    .Stroke(Color.DodgerBlue, 0.02);
page.SelectWithUserCell("OfficeIMO.Role", "Tier")
    .UserCell("OfficeIMO.Reviewed", "Yes", "STR");

doc.Save();

Callouts and annotations

Pages can add semantic callouts with leader connectors. OfficeIMO writes normal editable Visio shapes and connectors, plus User cells that make callouts easy to find again after loading.

using OfficeIMO.Visio;

var api = page.AddProcess(4, 4.5, 2, 1, "API");
var note = page.AddCallout(api, "api-note", "Check retry policy", 7.5, 6,
    new VisioCalloutOptions {
        Width = 2.4,
        Height = 0.8,
        RouteOffset = 0.15
    });
var autoNote = page.AddCallout(api, "sla-note", "Review SLA target",
    VisioSide.Right, gap: 0.35);

page.SelectCallouts()
    .LockPosition();

doc.Save();

Use the coordinate overload when you need exact placement, or the side-based overload when the callout should sit to the left, right, top, or bottom of the target shape without hand-calculating page coordinates.

Text styling

Text blocks can be styled with a reusable object or applied to selections. The same style model works for shape text and connector labels. Styles are saved into Visio ShapeSheet text block cells plus Char and Para sections, so text remains editable in Visio.

using OfficeIMO.Visio;
using Color = OfficeIMO.Drawing.OfficeColor;

var textStyle = new VisioTextStyle {
    FontFamily = "Aptos",
    Color = Color.FromRgb(0x33, 0x66, 0x99),
    Size = 12,
    Bold = true,
    HorizontalAlignment = VisioTextHorizontalAlignment.Center,
    VerticalAlignment = VisioTextVerticalAlignment.Middle,
    LeftMargin = 0.08,
    RightMargin = 0.08,
    TextPinY = -0.2,
    TextHeight = 0.4,
    BackgroundColor = Color.LightYellow,
    BackgroundTransparency = 20
};

page.AddProcess(2, 4, 2.5, 1, "Approve")
    .ApplyTextStyle(textStyle);

page.SelectWithData("Lane", "Finance")
    .ApplyTextStyle(textStyle);

var connector = page.AddConnector(source, target, ConnectorKind.Dynamic, VisioSide.Right, VisioSide.Left);
connector.Label = "Approved";
connector
    .PlaceLabel(0.55, offsetY: 0.18)
    .ApplyTextStyle(new VisioTextStyle {
        FontFamily = "Aptos",
        Color = Color.DodgerBlue,
        Size = 9,
        Bold = true,
        HorizontalAlignment = VisioTextHorizontalAlignment.Center,
        BackgroundColor = Color.White,
        BackgroundTransparency = 0
    });

page.SelectConnectedConnectors(source)
    .ApplyTextStyle(textStyle);

doc.Save();

Layout cleanup helpers

Selections can also be aligned, distributed, resized to text, centered, and fit to page bounds. Page fitting and centering include explicit connector waypoints and connector label boxes, so routed labels do not get clipped. Text sizing uses the deterministic OfficeIMO.Drawing measurement engine, so it works without system font APIs. Connector labels can also be moved deterministically away from page edges, unrelated shapes, and connector labels. The cleanup can slide labels along their connector path before falling back to page-coordinate offsets, runs whole-page label optimization passes that revisit the most conflicted labels first, and ignores generated adornment captions, so premium zone headers do not push legitimate connector labels around. It can also prefer connector labels inside the shared zone of their endpoints and away from unrelated background zones when premium zone diagrams need stronger label placement hints. It can also opt into obstacle-aware connector routing before label placement, which is useful when a generated diagram has simple connector-to-shape intersections.

using OfficeIMO.Drawing;
using OfficeIMO.Visio;

page.SelectWithData("Owner", "Ops")
    .ResizeToText(new OfficeFontInfo("Calibri", 11))
    .RelayoutAsGrid(columns: 2, horizontalSpacing: 0.4, verticalSpacing: 0.3)
    .Align(VisioVerticalAlignment.Middle);

page.SelectByMaster("Decision")
    .Align(VisioHorizontalAlignment.Center);

doc.AsFluent()
    .ExistingPage("Workflow", p => p
        .RelayoutShapesAsGrid(new[] { "review", "approve", "archive" }, layout => {
            layout.Columns = 3;
            layout.HorizontalSpacing = 0.4;
            layout.RouteInternalConnectors = true;
        })
        .RelayoutContainerMembers("runtime-tier", layout => {
            layout.Columns = 1;
            layout.VerticalSpacing = 0.25;
        }));

page.SelectConnectedConnectors(page.FindShapeById("approved")!)
    .ApplyTextStyle(new VisioTextStyle { FontFamily = "Calibri", Size = 9 })
    .ResizeLabelsToText(maximumWidth: 1.6);

page.PolishDiagram(new VisioDiagramPolishOptions {
    ResolveShapeOverlaps = true,
    ResolveConnectorShapeIntersections = true,
    ConnectorRoutingObstaclePadding = 0.12,
    ConnectorRoutingAvoidContainers = true,
    ConnectorRoutingAvoidBackgroundSurfaces = true,
    ConnectorRoutingAvoidConnectorCrossings = true,
    ConnectorRoutingPageOptimizationPasses = 3,
    PreferConnectorLabelsInsideEndpointZones = true,
    ConnectorLabelPositionStep = 0.08,
    ConnectorLabelMaxPositionShifts = 4,
    ConnectorLabelOptimizationPasses = 3,
    MaximumConnectorLabelWidth = 1.6,
    FitHorizontalMargin = 0.6,
    FitVerticalMargin = 0.45
});
doc.Save();

RelayoutAsGrid, RelayoutAsHorizontalStack, and RelayoutAsVerticalStack are deterministic and can reroute connectors whose endpoints are both inside a page-backed selection. The fluent page wrapper exposes the same engine for loaded diagrams through RelayoutShapesAsGrid, RelayoutShapesAsHorizontalStack, RelayoutShapesAsVerticalStack, RelayoutConnectedComponentAsGrid, and RelayoutContainerMembers, so common edit workflows can stay ID-based while still using typed selections under the hood. PolishDiagram can also move crowded top-level shapes apart before it resolves connector routes, connector labels, and page fitting, which is useful when a generated diagram has reasonable structure but still needs a final visual cleanup pass.

Learning from VSDX fixtures

OfficeIMO can inspect .vsdx files to learn which supported masters are present, then generate OfficeIMO-owned masters from code. The source file is a learning fixture, not a runtime template, so generated documents stay dependency-light and deterministic.

using OfficeIMO.Visio;

var doc = VisioDocument.Create("typed-shapes.vsdx");
doc.UseMastersByDefault = true;
doc.LearnMastersFromVsdx("DrawingWithShapes.vsdx",
    new[] { "Rectangle", "Ellipse", "Diamond", "Dynamic connector" });

var page = doc.AddPage("Page-1");
page.AddRectangle(2, 2, 2, 1, "Generated from OfficeIMO");
doc.Save();

Optional native Visio validation

On Windows machines with Microsoft Visio installed, you can add a stronger desktop compatibility gate without adding a compile-time Visio dependency. OfficeIMO uses late-bound COM automation only when you call this helper.

using OfficeIMO.Visio;

VisioDesktopValidationOptions options = VisioDesktopValidationOptions.RoundTripWithSvg();
options.SaveCopyPath = "diagram.visio-roundtrip.vsdx";
options.ExportDirectory = "visio-proof";

VisioDesktopValidationResult result = VisioDesktopValidator.Validate("diagram.vsdx", options);
if (result.IsAvailable && !result.IsValid) {
    throw new InvalidOperationException(string.Join(Environment.NewLine, result.Issues));
}

See OfficeIMO.Examples/Visio/* for more.

Feature Scope

  • 📄 Pages: ✅ add/remove pages
  • 🧱 Shapes: ✅ built-in and semantic shapes, master-backed stencil placements, groups, text, Shape Data, User cells, hyperlinks, protection, and local geometry preservation
  • 🔗 Connectors: ✅ dynamic, straight, right-angle, curved, side glue, explicit waypoints, arrows, labels, routing metadata, label placement, and page-level cleanup helpers
  • 🧭 Diagram builders: ✅ flowchart builder with vertical and two-column continuation layouts plus branch routing, ✅ generic graph builder with cycles, disconnected components, layered/grid/radial layouts, zones, package-backed stencil nodes, data-driven inventory/identity/Kubernetes/application dependency gallery graphs, and generated legends, ✅ block diagram builder with grid regions and data/control flows, ✅ architecture builder with infrastructure components, regions, and routed data/control/dependency flows, ✅ network builder with zones, devices, links, legends, record imports, Shape Data, and hyperlinks, ✅ sequence builder with participants, lifelines, message types, self-calls, activations, guarded/nested fragments, notes, and data-driven incident/runbook record imports, ✅ swimlane builder with lanes, phases, activities, handoffs, and exception paths, ✅ org chart builder with hierarchy, assistants, team bands, vacancies, and external roles, ✅ timeline builder with date-scaled milestones and span lanes
  • 🧰 Native stencils: ✅ built-in searchable catalogs for basic, flowchart, block-diagram, architecture, network, infrastructure, cloud, security/identity, containers/Kubernetes, data/platform, collaboration/business process, sequence, swimlane, org-chart, and timeline shapes; ✅ external package catalogs with learned dimensions, preview metadata, source package provenance, and native connection points
  • 🎨 Style themes: ✅ reusable shape/connector/text styles and Modern/Office/Fluent/Technical/Minimal/Dark/Print authoring presets
  • 🔎 Rich editing: ✅ recursive shape queries, shape/data/text/master/layer/hyperlink selectors, connector neighbor queries, page layers, shape and connector hyperlinks, typed stencil migration maps, bulk style/data/layer/hyperlink edits, align/distribute, resize-to-text, center content, and fit-to-content
  • 🖼️ Export/proof: ✅ dependency-free native SVG and PNG preview exports for OfficeIMO-authored pages; ✅ premium gallery baselines with PNG/SVG, inspection, and stencil-profile proof; ✅ showcase review proof with inspection, stencil profile, and visual-quality artifacts
  • 🧩 VSDX learning fixtures: ✅ inspect supported masters without treating sample files as runtime templates
  • 🧪 Validation: ✅ package/in-memory validators, visual quality analyzer, premium baseline lane, and optional Microsoft Visio desktop open, save-copy, and SVG/PNG/PDF export checks via late-bound COM

Authoring units

Pages now remember a DefaultUnit (inches by default). When you create a page with centimeters or millimeters, shape-adding overloads use that unit implicitly, and the fluent shape builders follow that page unit as well, so you don't need helper conversions:

var page = doc.AddPage("A4 landscape", 29.7, 21.0, VisioMeasurementUnit.Centimeters);
page.AddRectangle(4.0, 15.0, 4.0, 2.5, "Rectangle"); // all values in cm
page.AddCircle(16.0, 15.0, 3.5, "Circle");           // diameter in cm

If you prefer, you can still pass an explicit unit:

page.AddRectangle(1.5, 1.0, 2.0, 1.0, "Rect", VisioMeasurementUnit.Inches);

Connection points

You no longer need to add side connection points manually. The connector API ensures side glue automatically when you specify VisioSide.Left/Right/Top/Bottom. The old ensure method has been internalized.

At a glance

  • Create/Load/Save .vsdx (OPC packaging)
  • Build simple diagrams or data-driven premium diagrams without requiring Visio
  • Fluent builder: Page(...), Rect(...), Square(...), Ellipse(...), Circle(...), Diamond(...), Triangle(...), Connect(...)
  • Semantic builders for flowcharts, block diagrams, architecture, networks, topology, swimlanes, org charts, timelines, sequences, dependencies, and graphs
  • Built-in and package-backed stencil catalogs with searchable metadata and typed migration maps for loaded diagrams
  • Native SVG/PNG previews plus optional Microsoft Visio desktop validation/export proof
  • Showcase proof summaries through VisioShowcaseSummary, which writes Markdown, JSON, and browsable HTML artifact metadata for generated packages, previews, and review proof files, including headline proof/evidence totals plus diagram-level records/cards that pair packages with previews, SHA-256 hashes, native/desktop preview flags, inspection/stencil-profile/visual-quality proof flags, clean visual-quality and issue totals, shape/connector rollups, Shape Data key counts, semantic-kind counts, stencil-backed/basic-geometry mix, connection-point coverage, stencil provenance, complete review proof, and catalog coverage
  • Optional desktop preview artifacts are designed for explicit self-hosted Windows proof lanes where Microsoft Visio desktop automation is installed

Why OfficeIMO.Visio

  • Server-safe VSDX generation and reading using OPC + LINQ to XML
  • Diagram-first authoring APIs for real process, network, architecture, timeline, sequence, and graph scenarios
  • Dependency-light core with optional Visio desktop proof only when you ask for it
  • First-party stencils, premium styles, visual quality checks, and native SVG/PNG preview output for reviewable generated diagrams
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 is compatible.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on OfficeIMO.Visio:

Package Downloads
OfficeIMO.Reader.Visio

Visio adapter for OfficeIMO.Reader using OfficeIMO.Visio inspection snapshots.

OfficeIMO.ChartForgeX

Optional typed bridge from ChartForgeX visual artifacts to OfficeIMO Word, Excel, PowerPoint, PDF, drawing, and editable Visio surfaces.

OfficeIMO.Visio.Pdf

First-party loss-aware Visio to PDF conversion for OfficeIMO.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.2.7 38 8/29/2026
3.2.6 260 8/22/2026
3.2.5 323 8/21/2026
3.2.4 305 8/19/2026
3.2.3 234 8/17/2026
3.2.2 556 8/13/2026
3.2.1 312 8/11/2026
3.2.0 415 8/7/2026
3.1.1 210 8/7/2026
3.1.0 197 8/6/2026
3.0.3 244 7/27/2026
3.0.2 192 7/26/2026
3.0.1 450 7/26/2026
3.0.0 552 7/20/2026
2.0.1 342 7/14/2026
2.0.0 158 7/14/2026
1.0.15 286 7/9/2026
1.0.14 328 7/8/2026
1.0.13 539 7/5/2026
1.0.2 309 6/12/2026
Loading failed