VISION · Forge · SlideForge

Technical Documentation

Version 1.0 · .NET 8 · Namespace: VISION.Forge.SlideForge.* · All dependencies MIT-licensed

Installation & Setup

NuGet Pakete

dotnet add package VISION.Forge.SlideForge
dotnet add package VISION.Forge.SlideForge.Manipulation
dotnet add package VISION.Forge.SlideForge.Rendering
dotnet add package VISION.Forge.SlideForge.Charts
dotnet add package VISION.Forge.SlideForge.Export

Projektstruktur

VISION.Forge.SlideForge.sln
├── VISION.Forge.SlideForge.Core          Domain Models, Interfaces
├── VISION.Forge.SlideForge.OpenXml       PPTX Parser
├── VISION.Forge.SlideForge.Pdf           PDF Export
├── VISION.Forge.SlideForge.Rendering     PNG/JPEG Renderer
├── VISION.Forge.SlideForge.Manipulation  Editing + Template Engine
├── VISION.Forge.SlideForge.Charts        SVG Chart Generation
├── VISION.Forge.SlideForge.Export        HTML, SVG, XPS Export
├── VISION.Forge.SlideForge.Licensing     RSA/JWT Lizenz-System
├── VISION.Forge.SlideForge.Api           Öffentliche Fassade + DI
└── VISION.Forge.SlideForge.Tests         xUnit Test Suite

Abhängigkeiten (alle MIT)

PaketVersionVerwendung
DocumentFormat.OpenXml3.0.2PPTX Parser
PdfSharpCore1.3.65PDF Export
SkiaSharp2.88.8Rendering & Fonts
HarfBuzzSharp7.3.0Text Shaping
System.IdentityModel.Tokens.Jwt7.6.2Lizenz-JWTs

Licensing

Aktivierung

Hinweis: LicenseManager.Activate() muss einmalig beim Start der Anwendung aufgerufen werden, bevor SlideForge-Methoden genutzt werden.
using VISION.Forge.SlideForge.Licensing;

// Empfohlen: aus Environment Variable
LicenseManager.Activate(Environment.GetEnvironmentVariable("SLIDEFORGE_KEY")!);

// Ablaufdatum prüfen
if (LicenseManager.GetExpiryWarning() is {} msg)
  logger.LogWarning(msg);

Trial-Modus

Ohne Aktivierung läuft SlideForge im Trial-Modus: max. 3 Folien pro Export, Wasserzeichen auf PDFs. Alle Features bleiben testbar.

OEM / SaaS Aktivierung

// SaaS / Docker / Kubernetes
OemLicenseAdapter.ActivateSaas("SLIDEFORGE_LICENSE_KEY");

// Desktop-App (verschlüsselte Datei)
OemLicenseAdapter.ActivateDesktop();

// Docker Secret
OemLicenseAdapter.ActivateFromSecretFile("/run/secrets/slideforge-license");

// Automatisch (probiert alle Methoden in Reihenfolge)
var result = OemLicenseAdapter.ActivateAuto();
Air-Gap kompatibel: Endkunden benötigen keine Internetverbindung. Lizenzen werden vollständig offline via RSA-4096/JWT validiert.

PPTX Parsing

using VISION.Forge.SlideForge.OpenXml.Parsing;

var parser = PresentationParser.Create();
var doc    = parser.Parse("deck.pptx");        // aus Datei
var doc    = parser.Parse(stream);              // aus Stream
var doc    = await parser.ParseAsync("deck.pptx");

// Dokumentenmodell
doc.Width;        // Breite in Pixel (96 DPI)
doc.Height;       // Höhe in Pixel
doc.SlideCount;   // Anzahl Folien
doc.Slides;       // IReadOnlyList<SlideModel>

foreach (var el in doc.Slides[0].Elements)
{
  if (el is ShapeModel s)
    Console.WriteLine(s.Name);
}

Manipulation

using VISION.Forge.SlideForge.Manipulation;

using var m = PresentationManipulator.Open("deck.pptx");

// Folien verwalten
m.AddSlide(layoutIndex: 0)
 .InsertSlide(index: 2)
 .RemoveSlide(index: 0)
 .MoveSlide(fromIndex: 3, toIndex: 1)
 .DuplicateSlide(index: 0);

// Inhalt
m.ReplaceText("{{Platzhalter}}", "Wert")
 .AddTextBox(slideIndex: 0, x:100, y:200, width:400, height:50, text: "Hallo")
 .AddImage(slideIndex: 0, imageData: pngBytes, x:50, y:50, width:200, height:150)
 .SaveToFile("ausgabe.pptx");

Template Engine

Split-Run Merger: PowerPoint speichert {{token}} intern oft über mehrere XML-Runs. SlideForge führt diese automatisch zusammen – kein anderes Tool löst dieses Problem.
SyntaxBeschreibung
{{name}}Einfacher Wert
{{kunde.adresse.ort}}Verschachtelter Pfad
{{umsatz | format:"N0"}}Format-Filter
{{#each liste}} / {{/each}}Folie N-mal klonen
{{#if bedingung}} / {{/if}}Bedingter Inhalt
{{#slideIf bedingung}}Ganze Folie entfernen wenn false
using VISION.Forge.SlideForge.Api;

Presentation.Load("vorlage.pptx")
  .FillTemplate(new
  {
    KundenName  = "Acme GmbH",
    Umsatz      = 1_250_000m,
    Mitarbeiter = new[]
    {
      new { Name = "Alice", Rolle = "Engineering" },
      new { Name = "Bob"  , Rolle = "Design"      },
    }
  })
  .ExportToPdf("bericht.pdf");

Table Data Binding

using var m = PresentationManipulator.Open("vorlage.pptx");
m.BindTable(slideIndex: 2, "VerkaufsTabelle", daten,
  new TableBindOptions
  {
    OverflowMode               = TableOverflowMode.ContinueOnNextSlide,
    RepeatHeaderOnContinuation = true,
    ContinuationTitleSuffix    = " (Seite {page}/{total})",
    AlternatingRowColors       = ("FFFFFF", "F3F4F6"),
    ColumnFormats              = new() { [2] = "N0", [3] = "C2" },
    SummaryRow                 = new() { [0] = "Gesamt", [2] = "SUM" },
    NullPlaceholder            = "–"
  });
m.SaveToFile("ausgabe.pptx");

PDF Export

using VISION.Forge.SlideForge.Api;

Presentation.Load("deck.pptx")
  .ExportToPdf("ausgabe.pdf", pdf => pdf
    .ForPrint()
    .EmbedAllFonts()
    .WithMetadata(title: "Jahresbericht", author: "Max Mustermann"));

HTML · SVG · XPS Export

using VISION.Forge.SlideForge.Export;

// HTML – self-contained, mit Navigation und Speaker Notes
Exporter.Html.ExportToFile(doc, "praesentation.html");

// SVG – ein File pro Folie
Exporter.Svg.ExportToDirectory(doc, "/ausgabe/svg/");
Exporter.Svg.ExportCombined(doc, "deck.svg");

// XPS – für Druckworkflows, ECMA-388
Exporter.Xps.ExportToFile(doc, "praesentation.xps");
Exporter.Xps.ExportToFile(doc, "druck.xps", XpsExportOptions.HighRes);
byte[] xps = Exporter.Xps.ExportToBytes(doc);

PNG/JPEG Rendering

ModusBeschreibung
ContainGanzes Bild sichtbar (Letterbox), Seitenverhältnis erhalten
CoverFüllt Box, Ränder gecroppt, Seitenverhältnis erhalten
StretchStreckt auf genaue Boxgröße, verändert Seitenverhältnis
FitWidthBreite füllt Box, Höhe proportional
FitHeightHöhe füllt Box, Breite proportional
CenterNative Größe, zentriert
TileKachelmuster
UsePptxCropPowerPoint-eigener Crop-Wert
using VISION.Forge.SlideForge.Rendering.Options;

var renderer = SlideRenderer.Create(new RenderOptions
{
  Dpi                 = 192,           // Retina
  DefaultImageFitMode = ImageFitMode.Cover,
  ImageFitOverrides   = new()
  {
    ["FirmenLogo"] = ImageFitMode.Contain,
    ["Profilfoto"] = ImageFitMode.Cover
  }
});

foreach (var slide in renderer.RenderAll(doc))
  using (slide) slide.SaveToFile($"slide{slide.SlideIndex+1}.png");

Charts (14 Typen)

using VISION.Forge.SlideForge.Charts;

// Vertikal-Balken
string svg = ChartFactory.Bar.Vertical(new[]
{
  new DataPoint("Q1", 125_000),
  new DataPoint("Q2", 148_000),
}, new ChartOptions { Title = "Quartalsumsatz", NumberFormat = "N0" });

// Donut mit Centertext
string svg = ChartFactory.Donut.Build(
  slices, centerValue: "€ 1.2M", centerLabel: "Umsatz");

// KPI Gauge
string kpi = ChartFactory.Gauge.Build(
  value: 78, min: 0, max: 100, label: "Kundenzufriedenheit", unit: "%");

// Speichern
ChartFactory.SaveToFile(svg, "chart.svg");

Verfügbare Typen: Bar Vertical/Horizontal/Stacked · Line · Area · Pie · Donut · Gauge · Waterfall · Scatter · Bubble · Gantt · Heatmap · Radar

Connectors

using var m = PresentationManipulator.Open("diagramm.pptx");

// Auto-Routing zwischen Shapes
m.ConnectShapes(slideIndex: 0, fromShapeId: 10, toShapeId: 20);

// Org-Chart in einem Rutsch (Chaining)
m.ConnectShapes(0, ceoId, ctoId)
 .ConnectShapes(0, ceoId, cfoId)
 .ConnectShapes(0, ctoId, devId, options: ConnectorOptions.Flowchart);

m.SaveToFile("ausgabe.pptx");
m.AddHyperlinkToShape(0, shapeId: 5,
  HyperlinkTarget.Url("https://vision-software.io"));

m.AddHyperlinkToText(0, shapeId: 5, searchText: "hier klicken",
  HyperlinkTarget.Email("info@acme.de", subject: "Anfrage"));

// Alle Links inventarisieren
var links = m.GetAllHyperlinks();

// Domain migrieren
int count = m.UpdateHyperlinks("alte-domain.de", "neue-domain.de");

Password Protection

using VISION.Forge.SlideForge.Manipulation.Protection;

// Vollverschlüsselung (AES-256-CBC, PBKDF2-SHA512)
var enc = PresentationProtection.Encrypt(bytes, "Mein$tarkes#PW");
var dec = PresentationProtection.Decrypt(enc, "Mein$tarkes#PW");

// Bearbeitungsschutz (Datei lesbar, aber Passwort zum Bearbeiten)
var gesperrt = PresentationProtection.ApplyEditingProtection(bytes, "PW");
bool ok = PresentationProtection.ValidatePassword(bytes, "PW");

Speaker Notes

m.SetNotes(0, "Folie 1: Auf Q3-Rückgang eingehen.\nZahlen aus CFO-Bericht.");
m.AppendToNotes(0, "\n\nNachtrag: Neue Zahlen berücksichtigen.");
string? n    = m.GetNotes(0);
var     alle = m.GetAllNotes();    // Dictionary<int, string>
m.ClearNotes(0);

Metadata

var meta = m.GetMetadata();
m.SetMetadata(new PresentationMetadata
{
  Title   = "Q4 Jahresbericht 2024",
  Author  = "Max Mustermann",
  Company = "VISION",
  Keywords= "Finanzen, Q4, 2024",
  Custom  = { ["Projektnummer"] = "FIN-2024-Q4", ["Version"] = "1.2" }
});
// Nur einzelne Felder ändern (Rest bleibt):
m.UpdateMetadata(new PresentationMetadata { Company = "Neue GmbH" });

Comments

int id = m.AddComment(0, "Bitte Zahl prüfen", "Max", "max@acme.de",
               posX: 200, posY: 150);
var alle = m.GetAllComments();
m.DeleteComment(slideIndex: 0, commentId: id);
m.DeleteAllComments();  // alle Folien

Präsentationen zusammenführen

using var m = PresentationManipulator.Open("master.pptx");
m.AppendPresentation("intro.pptx")
 .AppendPresentation("finanzen.pptx")
 .AppendSlides("anhang.pptx", slideIndices: [0, 2])
 .InsertPresentation("titelfolie.pptx", insertAt: 0)
 .SaveToFile("final.pptx");

OEM / SaaS Deployment

Für OEM-Lizenzen: Endkunden sehen ausschließlich dein Produkt – kein Hinweis auf VISION. Kontakt: sales@vision-software.io
// SaaS / Docker / Kubernetes
// env: SLIDEFORGE_LICENSE_KEY=eyJhbGc...
OemLicenseAdapter.ActivateSaas();

// Docker Secret
// docker-compose: secrets: slideforge-license: external: true
OemLicenseAdapter.ActivateFromSecretFile("/run/secrets/slideforge-license");

// Desktop-App: Installer schreibt Key einmalig
OemLicenseAdapter.WriteDesktopLicenseFile(rawKey);
OemLicenseAdapter.ActivateDesktop();

ASP.NET Core Integration

// Program.cs
builder.Services.AddSlideForge(o =>
  o.LicenseKey = builder.Configuration["SlideForge:Key"]);

// Minimal API Endpoint
app.MapGet("/export/{id}", async (int id, Db db) =>
{
  var daten   = await db.GetReportData(id);
  using var ms = new MemoryStream();
  await Task.Run(() =>
    Presentation.Load("vorlage.pptx")
      .FillTemplate(daten)
      .ExportToPdf(ms));
  ms.Position = 0;
  return Results.File(ms, "application/pdf", $"bericht-{id}.pdf");
});

Architecture

Namespace: VISION.Forge.SlideForge.*

SlideForgeDocument (Immutable Domain Model)
       │
       ├── Slides[] → Elements[] (Shape, Image, Table)
       ├── Theme
       └── Width / Height

Eingabe:  PresentationParser  → SlideForgeDocument
                                      │
                          ┌───────────┴──────────────┐
                          ▼                          ▼
                   PdfExporter                SlideRenderer
                 (PdfSharpCore)               (SkiaSharp)
                          │                          │
                     PDF-Datei               PNG/JPEG/SVG

Manipulation: PresentationManipulator
              → Arbeitet direkt auf OOXML-XML
              → Unbekannte Elemente bleiben erhalten
              → Fluente, chainbare API

VISION · SlideForge · © 2025 · support@vision-software.io