Skip to content

Repository files navigation

DRIT.Drawing

A pure-managed .NET 2D graphics library with the same API surface as System.Drawing.Common, using an integrated font engine for all text measurement and glyph rendering — with no native dependencies and no third-party NuGet packages.

DRIT.Drawing is built around a vector domain model: any drawing performed through a Graphics surface can be captured as true vector output (SVG, EMF/WMF), not just rasterized to a bitmap. The same Graphics API produces raster, vector, or metafile output unchanged.

.NET License: MIT Tests


Table of Contents


What is DRIT.Drawing?

DRIT.Drawing is a pure-managed .NET 2D graphics library. It does not depend on System.Drawing.Common, on libgdiplus, or on any third-party NuGet package. The entire stack — rasterizer, image codecs, font engine, metafile parser, SVG reader/writer, and the Unicode bidirectional algorithm — is implemented in source.

Key positioning:

  • System.Drawing.Common API surface, mirrored in the DRIT.Drawing namespace. Graphics, Bitmap, Pen, Brush, Font, Color, Matrix, GraphicsPath, Region, Image/Metafile — consumers can alias with using SD = DRIT.Drawing.
  • Vector-first architecture. A format-agnostic VectorScene sits between every producer (Graphics recording, EMF/WMF playback, SVG parsing) and every consumer (rasterizer, SVG writer, EMF writer). Any Graphics drawing can be emitted as true vector SVG or recorded as EMF/WMF.
  • Deep format fidelity. Full in-house WMF/EMF/EMF+ metafile engine; managed PNG/BMP/GIF/TIFF/ICO codecs; a hand-written JPEG codec (baseline, progressive, encoder) with no external JPEG dependency.
  • First-class text. Integrated TrueType/OpenType/CFF/Type1/WOFF/WOFF2 font engine, full Unicode Bidirectional Algorithm (UAX #9), automatic font fallback, rich text runs, and text-on-path.
  • Zero external dependencies. No NuGet packages, no native libraries, no System.Drawing.Common installation required.

Features

Core Graphics Types

Type Description
Color ARGB packed uint, 174 known colors, HSV conversion
Point/PointF Integer/float coordinate structs with arithmetic
Size/SizeF Dimension structs with arithmetic
Rectangle/RectangleF Bounding box structs with intersection/union
Matrix Affine transformation (rotate, scale, translate, shear, invert)

Drawing Primitives

Type Description
Pen Stroke with color, width, line caps, joins, dash patterns
Brush Fill styles: SolidBrush, HatchBrush, LinearGradientBrush, PathGradientBrush, TextureBrush
GraphicsPath Vector paths with lines, Bézier curves, arcs, ellipses
Region Constructive solid geometry (CSG) for clipping

Image Support

Format Status
BMP ✅ Full read/write support
PNG ✅ Managed codec with zlib compression
APNG ✅ Animated PNG — read/write, multi-frame, dispose/blend ops
GIF ✅ LZW compression, animation support
TIFF ✅ Basic read/write (no-compression, LZW)
ICO ✅ Multi-size icon support
JPEG ✅ Native decoder/encoder (SOF0/SOF1/SOF2) — no external JPEG dependency
SVG ✅ Read/write — true vector, not embedded raster
WMF/EMF/EMF+ ✅ Read/write — playback and recording

Text Rendering

Feature Description
Font/FontFamily Integration with the built-in font engine for metrics and glyph access
StringFormat Alignment, trimming, tab stops, hotkey prefix
DrawString Full text layout with word breaking, alignment
MeasureString Text measurement for layout
Bidi (UAX #9) Full Unicode Bidirectional Algorithm — RTL scripts and mixed-direction text
Font Fallback Automatic glyph substitution from a fallback font chain
Rich Text Runs Mixed fonts, sizes, styles, and brushes in a single layout
Text on Path Render text along an arbitrary vector baseline

Vector Graphics

Feature Description
VectorScene Format-agnostic domain model of recorded drawing commands
RecordingGraphics A Graphics surface that captures vector commands instead of rasterizing
SvgImage Vector Image subclass — load/save SVG, rasterize, or convert to EMF
SVG Writer VectorScene → SVG (path/gradient/pattern/clip/text/image elements)
SVG Reader SVG → VectorScene (streaming XmlReader-based, tolerant)
EMF/WMF Bridge Bidirectional transcoding between VectorScene and metafile records
EMF/WMF Recording Capture any Graphics drawing as EMF/WMF (previously playback-only)

Advanced Features

Feature Description
Metafile EMF/WMF/EMF+ playback and recording using the in-house metafile engine
Printing PrintDocument, PageSettings, PrinterSettings stubs
Icon/Cursor Icon extraction and cursor support
Antialiasing 4× MSAA for smooth rendering
ImageAttributes Color matrix, gamma, color key, remap table, threshold

Supported File Formats

Format Read Write Specification
BMP Bitmap Image Format
PNG Portable Network Graphics (managed zlib)
APNG Animated PNG (Mozilla APNG 1.0)
JPEG Baseline (SOF0/SOF1) + Progressive (SOF2)
GIF GIF87a/GIF89a (managed LZW, animation)
TIFF No-compression + LZW
ICO Multi-size icon/cursor
SVG SVG 1.1 (write) / SVG 1.1 + selective SVG 2 (read)
WMF Windows Metafile ([MS-WMF])
EMF Enhanced Metafile ([MS-EMF])
EMF+ Enhanced Metafile Plus

Platform Independence

DRIT.Drawing is implemented in pure managed C# and targets netstandard2.0 and net6.0, so it runs on:

  • .NET Framework (via netstandard2.0)
  • .NET Core / .NET 5+ (via netstandard2.0 or net6.0)
  • Windows, Linux, and macOS

It has no external dependencies — no NuGet packages, no native libraries, and no System.Drawing.Common installation required.


Get Started

Basic Drawing

using var bitmap = new Bitmap(800, 600);
using var graphics = Graphics.FromImage(bitmap);

// Draw shapes
using var pen = new Pen(Color.Blue, 2);
graphics.DrawLine(pen, 0, 0, 800, 600);
graphics.DrawEllipse(pen, 100, 100, 200, 200);

// Fill shapes
using var brush = new SolidBrush(Color.Red);
graphics.FillRectangle(brush, 50, 50, 100, 100);

// Draw text
using var font = new Font("Arial", 16, FontStyle.Bold);
graphics.DrawString("Hello, World!", font, brush, new PointF(200, 200));

bitmap.Save("output.png", ImageFormat.Png);

Transformations

var matrix = new Matrix();
matrix.Rotate(45);
matrix.Translate(100, 100);
graphics.Transform = matrix;

Gradients

var brush = new LinearGradientBrush(
    new PointF(0, 0),
    new PointF(100, 100),
    Color.Red,
    Color.Blue);
graphics.FillRectangle(brush, 0, 0, 100, 100);

Text Layout

var format = new StringFormat
{
    Alignment = StringAlignment.Center,
    LineAlignment = StringAlignment.Center
};
graphics.DrawString("Centered text", font, brush, layoutRect, format);

Draw to Vector SVG

Any Graphics drawing can be captured as true vector SVG — no rasterization occurs until RenderToBitmap() is explicitly called.

using var svg = new SvgImage(800, 600);          // vector canvas
using var g = svg.CreateGraphics();               // RecordingGraphics, same API as Graphics
g.Clear(Color.White);
g.DrawEllipse(new Pen(Color.Blue, 2), 100, 100, 200, 200);
using var grad = new LinearGradientBrush(
    new Point(0, 0), new Point(100, 100), Color.Red, Color.Blue);
g.FillRectangle(grad, 50, 50, 100, 100);
g.DrawString("Hello", new Font("Arial", 16), Brushes.Black, 200, 200);
svg.Save("drawing.svg", ImageFormat.Svg);         // true vector SVG

Convert SVG, EMF, and Bitmap

The VectorScene is the single pivot for all vector conversions — no pairwise converters are written.

// Load SVG, rasterize, or convert to EMF
using var svg = new SvgImage("input.svg");
svg.RenderToBitmap().Save("output.png", ImageFormat.Png);
svg.Save("output.emf", ImageFormat.Emf);          // SVG → EMF

// Convert EMF to SVG
using var emf = new Metafile("chart.emf");
emf.Save("chart.svg", ImageFormat.Svg);

// Convert any Bitmap to (embedded-image) SVG
using var bmp = new Bitmap("photo.png");
bmp.Save("photo.svg", ImageFormat.Svg);

// Decode SVG through the codec registry (rasterized)
using var raster = new Bitmap("drawing.svg");

Animated PNG (APNG)

APNG is exposed through the System.Drawing-style multi-frame API on Image/Bitmap (GetFrameCount/SelectActiveFrame with FrameDimension.Time) — the same surface System.Drawing uses for animated GIF. A non-APNG-aware caller still sees the default image; animation frames are reachable via FrameDimension.Time.

using var bmp = new Bitmap("animated.apng");

int frames = bmp.GetFrameCount(FrameDimension.Time);
for (int i = 0; i < frames; i++)
{
    bmp.SelectActiveFrame(FrameDimension.Time, i);
    bmp.Save($"frame{i}.png", ImageFormat.Png);
}

Encoding a multi-frame Bitmap as PNG automatically emits APNG (acTL/fcTL/fdAT); a single-frame Bitmap produces a plain PNG, byte-identical to before.

Text on a Path

Render text along an arbitrary vector baseline, with each glyph positioned and rotated tangent to the path.

using var circle = new GraphicsPath();
circle.AddEllipse(100, 50, 400, 300);
g.DrawStringOnPath("Around the circle we go",
    new Font("Arial", 20f), brush, circle,
    new StringFormat { Alignment = StringAlignment.Center });

// Above the path, with a start offset
using var curve = new GraphicsPath();
curve.AddBezier(20, 300, 150, 100, 450, 500, 580, 300);
g.DrawStringOnPath("Above the curve",
    new Font("Arial", 18f), brush, curve, null,
    new TextOnPathOptions { StartOffset = 30f, PerpendicularOffset = 20f });

// Produce a fillable path (metafile-recordable)
using var textPath = new GraphicsPath();
textPath.AddStringOnPath("Fillable", new FontFamily("Arial"), 0, 24f, curve);
g.FillPath(new SolidBrush(Color.Red), textPath);

Rich Text, Bidi, and Font Fallback

Existing DrawString now performs bidi reordering (UAX #9) and font fallback by default. Rich text runs (mixed fonts/styles/colors) are available via DrawRichText.

// RTL Hebrew — renders right-to-left automatically
g.DrawString("שלום עולם", new Font("Arial", 24f), brush,
    new RectangleF(0, 20, 600, 40),
    new StringFormat(StringFormatFlags.DirectionRightToLeft));

// Fallback — CJK chars render from a fallback font even though Arial lacks them
g.DrawString("Hello 世界", new Font("Arial", 24f), brush, 20f, 80f);

// Rich text — mixed fonts/styles/colors
var rich = new RichTextOptions(new FontFamily("Arial"), 18f)
{
    Runs =
    {
        new RichTextRun(0, 6),                                   // "Hello " — default
        new RichTextRun(6, 5) { FontStyle = FontStyle.Bold },    // "World" — bold
        new RichTextRun(11, 4) { EmSize = 28f },                 // "Big!" — larger
        new RichTextRun(15, 5) { Brush = new SolidBrush(Color.Red) }, // "Red!" — red
    }
};
g.DrawRichText("Hello WorldBig!Red!", rich, brush, new RectangleF(20, 160, 560, 60));

// Custom fallback chain
g.FontFallback = new FontFallbackChain
{
    new FontFamily("Segoe UI Symbol"),
    new FontFamily("Segoe UI Emoji"),
};

Architecture

DRIT.Drawing/
├── Colors/         # Color, KnownColor, SystemColors
├── Geometry/       # Point, PointF, Size, SizeF, Rectangle, RectangleF
├── Drawing2D/      # Matrix, GraphicsPath, Region, brushes, pens
├── Pens/           # Pen, Pens, CustomLineCap, AdjustableArrowCap
├── Brushes/        # Brush, SolidBrush, TextureBrush, Brushes
├── Text/           # Font, FontFamily, StringFormat, text layout
│   └── Bidi/       # UAX #9 Bidirectional Algorithm + character tables
├── Imaging/        # Image, Bitmap, Icon, Cursor, Metafile, SvgImage
├── Graphics/       # Graphics, RecordingGraphics, IDrawingContext
├── Rendering/      # Scanline rasterizer, image renderer, EmfPlayer/WmfPlayer
├── Codecs/         # BMP, PNG/APNG, GIF, TIFF, JPEG, ICO, SvgCodec
│   ├── Png/        # APNG chunk parsing + frame builder
│   └── Jpeg/       # Native marker parser, baseline/progressive decoder, encoder
├── Vector/         # VectorScene + DrawingCommand hierarchy (vector domain model)
│   ├── Commands/   # DrawPath/FillPath/DrawImage/DrawString/transform/clip/state
│   ├── Svg/        # SvgReader, SvgWriter, brush/pen/path/transform/clip/text/image
│   └── Emf/        # EmfDrawingContext, WmfDrawingContext, EmfRecordMapper
├── Metafile/       # In-house WMF/EMF/EMF+ parser + writer
├── Font/           # Integrated font engine (TTF/OTF/CFF/Type1/WOFF/WOFF2)
└── Printing/       # PrintDocument, PageSettings, PrinterSettings

The Vector Domain Model

The Vector/ directory is a format-agnostic recording of drawing operations that sits between every producer and every consumer. This turns the conversion matrix from O(N²) pairwise converters into O(2N) readers + writers against the common model:

                ┌─────────────────┐
   Graphics ──► │                 │ ──► Rasterizer (Bitmap)
   EMF/WMF ──►  │   VectorScene   │ ──► SvgWriter  (.svg)
   SVG ──────►  │  (domain model) │ ──► EmfWriter  (.emf/.wmf)
   (future) ──► │                 │ ──► (future: Pdf, Xps)
                └─────────────────┘
  • RecordingGraphics : Graphics overrides the core draw methods to append commands to a VectorScene instead of rasterizing. Existing drawing code produces vector output unchanged.
  • VectorScenePlayer replays a scene onto any IDrawingContext — a raster Graphics, an SvgDrawingContext, or an EmfDrawingContext. One replay engine, three outputs.
  • EMF/WMF recording — previously EmfCodec.Encode could only play back metafiles, never record them. The RecordingGraphicsVectorSceneEmfDrawingContext path emits any Graphics drawing as EMF/WMF.

Integration

Font Engine

Text rendering uses the integrated font engine (source-level, no external dependency) for:

  • Font loading from files or byte arrays in TTF, OTF, CFF, Type1, WOFF, and WOFF2 formats
  • Font metrics (IFontMetrics) — ascender, descender, line gap, units per EM
  • Glyph outlines (GlyphOutlineRenderer, IGlyphOutlinePainter)
  • Unicode to glyph mapping (IFontEncoding) with HasGlyph/TryGetGlyph for fallback detection
  • Font collections (TTC) and font subsetting

Metafile Engine

EMF/WMF/EMF+ playback and recording uses the in-house metafile engine for:

  • Record parsing (MetafileReader) and writing (MetafileWriter)
  • GDI command translation to Graphics/IDrawingContext calls (EmfPlayer, WmfPlayer)
  • Bidirectional bridging to the VectorScene (EmfDrawingContext, WmfDrawingContext)

Building

dotnet build DRIT.Drawing/DRIT.Drawing.csproj
dotnet test DRIT.Drawing.Tests/DRIT.Drawing.Tests.csproj

Requires .NET 8.0 SDK for tests. The library targets netstandard2.0 + net6.0.

License

DRIT.Drawing is licensed under the MIT License.

About

Pure-managed .NET 2D graphics library mirroring System.Drawing.Common — vector-first architecture (SVG, EMF/WMF), integrated font engine (TTF/OTF/CFF/WOFF), image codecs (PNG/APNG/JPEG/GIF/TIFF), and UAX #9 bidi. Zero external dependencies.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages