OfficeIMO.Core 3.5.2

Prefix Reserved
dotnet add package OfficeIMO.Core --version 3.5.2
                    
NuGet\Install-Package OfficeIMO.Core -Version 3.5.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.Core" Version="3.5.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="OfficeIMO.Core" Version="3.5.2" />
                    
Directory.Packages.props
<PackageReference Include="OfficeIMO.Core" />
                    
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.Core --version 3.5.2
                    
#r "nuget: OfficeIMO.Core, 3.5.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.Core@3.5.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.Core&version=3.5.2
                    
Install as a Cake Addin
#tool nuget:?package=OfficeIMO.Core&version=3.5.2
                    
Install as a Cake Tool

OfficeIMO.Core - shared document, security, data, and drawing primitives

nuget version nuget downloads

OfficeIMO.Core is the zero-dependency shared foundation for OfficeIMO packages. It owns common document lifecycle and package-safety contracts, neutral tabular row mapping, color conversion, image metadata, font and text measurement, vector scenes, reusable ink and math models, chart snapshots, SVG, raster canvases, PNG/JPEG/TIFF/WebP encoding, and drawing-quality primitives. Format packages keep their file-format behavior while reusing these document-agnostic models and renderers.

Bounded embedded payload reads

Native document owners create OfficeEmbeddedPayloadInspection sessions through InspectEmbeddedPayloads(options, cancellationToken). Core owns their common descriptor and cumulative read contract; the format owner discovers and opens its own payloads. No consumer needs to reopen private package parts.

using OfficeIMO;

using OfficeEmbeddedPayloadInspection payloads = document.InspectEmbeddedPayloads(
    new OfficeEmbeddedPayloadInspectionOptions {
        MaxPayloads = 128,
        MaxPayloadBytes = 16L * 1024 * 1024,
        MaxTotalPayloadBytes = 64L * 1024 * 1024,
        MaxReadBytes = 64L * 1024 * 1024
    });
foreach (OfficeEmbeddedPayloadInfo payload in payloads.Payloads) {
    Console.WriteLine($"{payload.Id}: {payload.ContentType}");
    if (payload.SuggestedFileName.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase)) {
        using Stream bytes = payloads.OpenRead(payload.Ordinal);
        // Pass the selected document to the application's bounded consumer.
    }
}

Hashing is off by default. An inventory can report Length = -1 and HasKnownLength = false when the native owner cannot obtain the decoded size without reading the payload. Unknown sizes are admitted during a selected read; they never bypass the member or aggregate limits. IncludeSha256 = true requests a bounded scan, and that scan shares MaxReadBytes with subsequent and repeated reads. Distinct owner relationships remain distinct descriptors.

Keep the native document open while requesting new reads. Each returned stream is a complete read-only detached copy; the caller disposes it, and it remains usable after either the document or inspection session is disposed. Disposing a session rejects new reads and releases its source callbacks. Limit failures retain the embedded-payload owner, limit name and maximum in OfficeInspectionResourceLimitException. External references are never fetched and payloads are never executed. Native source, parser and expansion limits apply separately; these byte ceilings do not measure all managed model allocations.

The assembly was previously named OfficeIMO.Drawing. Drawing became the original shared foundation because keeping these primitives together avoided a separate Core → Drawing → format dependency chain. As lifecycle, security, package, and data contracts accumulated, the package name stopped describing its actual responsibility. That same single zero-dependency foundation is now named OfficeIMO.Core; it was not split into another runtime dependency. Actual drawing APIs remain in OfficeIMO.Drawing, neutral data and flattening contracts use OfficeIMO.Data, security APIs remain in OfficeIMO.Security, and cross-document lifecycle, compatibility, capability, and conversion-report contracts use the root OfficeIMO namespace.

The managed VP8 decoder is maintained in OfficeIMO.Core under OfficeIMO's MIT license. It was ported from the same author's CodeGlyphX implementation. The lossy VP8 encoder is adapted from CodeGlyphX under Apache-2.0 and retains its source notices. Both implementations run inside Core without a CodeGlyphX runtime dependency. The third-party notices identify the incorporated code and its license files.

For bounded image sequences, format discovery, resize planning, shared text options, metadata-aware encoding, comparison, and visual fingerprints, see managed raster workflows. The examples use owned image types and document buffer and stream ownership, orientation, cancellation, and metadata omissions.

Install

dotnet add package OfficeIMO.Core

Portable report drawings

OfficeDrawingPortableExporter exports a bounded OfficeDrawing as version 1 JSON for the JavaScript PDF drawing block. It preserves ordered solid-color paths, positioned Latin text, rectangular clips and positive uniform scaling. The producer and JavaScript consumer must load the same licensed static TrueType faces. Register those faces in drawing.Fonts and select their exact family:

using OfficeIMO.Drawing;

var options = new OfficeDrawingPortableOptions("Report Sans");
if (!OfficeDrawingPortableExporter.TryExportJson(drawing, options, out string? json, out var diagnostics)) {
    throw new NotSupportedException(string.Join("; ", diagnostics.Select(item => item.Message)));
}
File.WriteAllText("chart.json", json!);

The exporter rejects unsupported elements before returning a payload. Gradients, transparency, images, nonrectangular clips, rotated or wrapped text, variable/color fonts and complex text shaping are outside this profile. Solid strokes retain caps and joins; miter joins require a miter limit of ten. Pair positioning uses the existing Core font metrics; adjusted Latin runs become individually positioned characters. Fonts are supplied separately. Resource options bound elements, commands, nesting, text and JSON size. Drawing dimensions cannot exceed 14,400 points; coordinates must be finite and within one million points.

Native VBA projects

OfficeVbaProject in the OfficeIMO namespace reads and writes the MS-OVBA compound project used by Office documents. It uses the existing managed compound storage and signature-binding infrastructure. Source editing requires no Python, Office installation, COM automation, or additional runtime package.

using OfficeIMO;

var project = OfficeVbaProject.Create("Automation");
project.AddModule("Helpers",
    "Public Function Value() As Long\r\nValue = 42\r\nEnd Function\r\n");
project.AddModule("Counter", "Public Count As Long\r\n", OfficeVbaModuleKind.Class);
project.AddRegisteredReference("Office",
    new Guid("2DF8D04C-5BFA-101B-BDE5-00AA0044DE52"), 2, 8);
File.WriteAllBytes("vbaProject.bin", project.Write().GetBytes());

var loaded = OfficeVbaProject.Load(File.ReadAllBytes("vbaProject.bin"));
loaded.ExportSources("automation-source"); // Destination must be new.
// Edit the numbered .bas/.cls files; vba-project.xml retains logical identities.
loaded.ImportSources("automation-source");
loaded.RenameModule("Helpers", "Tools");
File.WriteAllBytes("edited-project.bin", loaded.Write().GetBytes());

Modules exposes logical names, persistence kinds, and source including persisted Attribute lines. SetModuleSource, AddModule, RenameModule, DeleteModule, AddRegisteredReference, and RemoveReference operate on a detached model. Module and reference lookup is case-insensitive. Renaming and reference removal do not rewrite statements that use the old names or types; callers update those statements explicitly. Adding a reference writes its identity without installing or resolving a type library.

Operation Contract
Read Bounded directory, reference, standard/class, document, and designer-source decoding. Unsupported directory records fail explicitly.
Create Template-free project, standard modules, ordinary classes, and explicit registered references. Document adapters own host identities.
Edit Replace source; rename/delete standard and ordinary class modules. Document/designer source retains its existing identity and opaque design.
Preserve An unchanged write returns the exact original bytes. Edits retain unrelated streams, storage metadata, reference records, and untouched token-compressed module stream bytes. Existing raw source chunks are normalized without changing the source or its stream prefix.
Import/export UTF-8 .bas/.cls source with an XML manifest. Import validates the entire batch before changing the model; omitted modules remain present.
Protection Protected projects are readable and support unchanged writes. Mutation is rejected; protection is never bypassed or removed.
Signatures The containing document adapter rejects a changed signed project unless AllowSignatureRemoval is explicit. Re-sign after editing, before signing the whole package.

Modules with source changes are rewritten without compiled prefixes. Writes clear project compilation state and __SRP_ caches so Office rebuilds them. Source uses CRLF and the project's code page with strict character checks. Windows-1250 and Windows-1252 are built in; other code pages must be available in the application's runtime or its already registered encoding provider. The writer uses token-compressed chunks and adapts their length for incompressible source, preserving the source bytes without inserting padding. Reading existing raw chunks and unchanged-byte preservation remain supported.

Input/output compound bytes and aggregate expanded directory/source each default to 64 MiB. Configure OfficeVbaReadOptions and OfficeVbaWriteOptions for a different bounded workload. New module identifiers are ASCII VBA identifiers up to 31 characters; existing Unicode module identities remain readable.

Document adapters retain a bounded recovery snapshot of the existing VBA payload and child parts while applying an update. OfficeVbaWriteOptions.MaximumRecoveryBytes controls that separate limit, which defaults to 64 MiB; MaximumProjectBytes continues to bound the new output, so a smaller replacement can replace a larger existing project. Media data-part references in that VBA subgraph are rejected before mutation. Recoverable storage failures restore the prior payload and child parts, including Word's final named-module removal. If the backing storage also prevents restoration or cleanup, the adapter throws an AggregateException containing both failures; discard that document instance.

Word, Excel, and PowerPoint expose ReadVbaProject() and SetVbaProject(OfficeVbaProject, ...) over this same model. See their package READMEs for document usage. This API edits source and preserves existing form designers; it does not compile or execute VBA, create form designers, or provide an Access database carrier.

Mathematical drawing

OfficeMathRenderer renders the owned equation model using caller-supplied fonts. Register a mathematical face in OfficeMathRenderOptions.Fonts and select its family through Font. OpenType MATH constants, glyph variants and assemblies determine fraction rules, script spacing, stretched delimiters, radicals and accents when available. Fonts without those tables use the existing geometric fallback.

UseFontMathMetrics selects font-derived layout, including available stretched glyph variants and assemblies. Disable it explicitly when the application requires the geometric fallback. Rendering cancellation applies during font measurement and equation construction. These options do not execute scripts or require an external rendering engine.

Per-column row mapping

RowMapper<T> in OfficeIMO.Data provides explicit assignments for any DbDataReader. CSV and Excel use this shared mapper. FromColumns tries aliases in the supplied order; required missing columns and ambiguous duplicate matches fail before row projection.

using OfficeIMO.Data;
using System.Data.Common;
using System.Globalization;

static IEnumerable<Invoice> ReadInvoices(DbDataReader reader) =>
    reader.RowsAs<Invoice>(map => map
        .FromColumns<int>(new[] { "Id", "InvoiceId" },
            (row, value) => { row.Id = value; return row; })
        .FromColumn<decimal>("Amount",
            (row, value) => { row.Amount = value; return row; },
            new RowMappingColumnOptions { Culture = CultureInfo.GetCultureInfo("fr-FR") })
        .FromColumn<DateTime>("Date",
            (row, value) => { row.Date = value; return row; },
            new RowMappingColumnOptions { DateTimeFormats = new[] { "yyyyMMdd" } })
        .FromColumn<string>("Note",
            (row, value) => { row.Note = value; return row; },
            new RowMappingColumnOptions { Optional = true }));

public sealed class Invoice {
    public int Id { get; set; }
    public decimal Amount { get; set; }
    public DateTime Date { get; set; }
    public string? Note { get; set; } = "No note";
}

Optional means the source column may be absent. Its assignment is skipped, preserving the model's initialized value. Present empty or invalid values still follow the normal conversion and nullability rules. Even when every configured column is optional and absent, each source row produces a model.

The mapper snapshots each binding's culture and date-format list when it is configured. Per-column date/time formats replace the reader's format list and are tried before ordinary culture-based parsing. A per-column TypeConverter replaces the reader's converter for that binding. Returning (false, null) selects built-in conversion; handled results, including null, take precedence. The converter receives the value exposed by the reader before built-in conversion. Excel numeric mappings retain the original date serial when a converter declines.

The same controls apply to RowsAs, RowsAsAsync and RowsAsParallel. Parallel converters must support concurrent calls. Source-value redaction continues to follow the reader's mapping-error policy.

UTF-8 field text

On .NET 8 and later, OfficeIMO.Data provides UTF-8 text access for any IDataRecord. TryGetUtf8Text discovers the optional IDataReaderUtf8TextSource capability and borrows text from provider-owned UTF-8 storage. A provider may initialize or update a text cache when a field is requested, including encoding decoded strings and allocating UTF-8 storage. Consume the returned span before advancing or closing the reader, or copy it when the bytes must survive those operations. A provider can decline individual fields; the ordinary getters remain available. A false result does not identify a null, and an empty text field can return true with an empty span.

GetUtf8Bytes copies text into a buffer that the caller owns. It uses borrowed bytes when available and otherwise encodes the provider's GetString result with standard UTF-8 replacement handling and bounded temporary storage. String conversion, null handling and cursor validation follow that provider's contract. The existing IDataRecord.GetBytes method retains its provider's binary contract.

using OfficeIMO.Data;
using System.Data;
using System.IO;

static void WriteFieldText(IDataRecord row, int ordinal, Stream destination) {
    byte[] buffer = new byte[4096];
    long offset = 0;
    while (true) {
        int copied = (int)row.GetUtf8Bytes(ordinal, offset, buffer, 0, buffer.Length);
        if (copied == 0) break;
        destination.Write(buffer, 0, copied);
        offset += copied;
    }
}

Offsets and lengths refer to bytes, so a copied segment can split a multi-byte character. Passing a null buffer returns the total encoded byte length. An offset at or beyond the end returns zero; negative offsets and lengths, and destination segments outside the supplied buffer, are rejected.

Factory mapping for borrowed models

On .NET 10, RowsAsBorrowed<T> projects unread rows through a caller factory, including models declared as ref struct. Resolve column ordinals once and construct the model with the provider's ordinary getters and borrowed-text API:

using OfficeIMO.Data;
using System;
using System.Data.Common;
using System.Threading;

static long SumBorrowedInvoices(DbDataReader reader, CancellationToken cancellationToken) {
    int name = reader.GetOrdinal("Name"), id = reader.GetOrdinal("Id");
    long total = 0;
    foreach (BorrowedInvoice invoice in reader.RowsAsBorrowed<BorrowedInvoice>(row => {
        if (!row.TryGetUtf8Text(name, out ReadOnlySpan<byte> text))
            throw new InvalidOperationException("The name field does not offer borrowed UTF-8 text.");
        return new BorrowedInvoice(text, row.GetInt32(id));
    }, cancellationToken)) {
        total += invoice.Id + invoice.Name.Length;
    }
    return total;
}

public readonly ref struct BorrowedInvoice {
    public BorrowedInvoice(ReadOnlySpan<byte> name, int id) { Name = name; Id = id; }
    public ReadOnlySpan<byte> Name { get; }
    public int Id { get; }
}

The factory runs once per successful advance; repeated Current access returns the cached model. Borrowed fields remain valid only until the reader advances, changes results, closes, or is disposed. Consume them within the loop or copy the fields that need to survive it. The caller retains ownership of the reader after completion, early exit, cancellation, or factory failure. Cancellation is observed between rows and before factory invocation; a running synchronous read or factory cannot be interrupted. Getter and factory exceptions propagate unchanged, and schema conversion follows the getters used by the factory.

This synchronous pattern enumeration has no asynchronous, parallel, or materialization contract. Automatic ref-struct property or constructor mapping is unsupported; ordinary models continue to use RowsAs<T> and RowMapper<T>.

Image export density

OfficeImageExportOptions.UseQuality(...) and fluent WithQuality(...) select shared density presets: Preview is 96 DPI, Screen is 192 DPI, and Print is 300 DPI. They retain the selected fonts, layout, format, and safety limits. Clear TargetDpi when setting Scale directly; fluent WithScale(...) clears it automatically. Each document adapter defines its logical units per inch.

OfficeDrawing.ExportImage(format, options) exports a detached drawing through the same raster limits, density metadata, codecs, deadline, and diagnostic policy. Raster output is rendered at the requested density. SVG retains vector geometry and text; it cannot add detail to embedded raster images. Register regular and bold font faces for consistent measurement and output across machines.

Interactive OfficeDrawing links accept HTTP(S), mail, telephone, and local relative or fragment targets. SVG import keeps the painted content but omits links with executable, data, file, or other unsupported schemes; direct AddLink calls reject those targets. SVG export retains accepted links as interactive anchors.

Raster strokes preserve fractional widths, caps, joins, miter limits, and dash phase. The renderer paints overlapping pieces of one stroke together, so an extra point or intersecting subpath does not darken translucent ink. Affine transforms apply to the stroke outline. Curve detail follows output density, with a target error of 0.05 device pixels and a bounded segment count. Thin contour details contribute their area even when they fall between sample rows. Image reduction averages premultiplied colors before bilinear placement; explicit nearest-neighbor drawing retains its pixel-art behavior.

OfficeFontInfo.Face carries numeric weight, stretch, and slant through measurement, raster painting, and SVG export. Register the faces a document needs and request the descriptor directly:

var drawing = new OfficeDrawing(240, 80);
drawing.Fonts.Add("Inter", File.ReadAllBytes("Inter-Regular.ttf"), new OfficeFontFaceDescriptor(400));
drawing.Fonts.Add("Inter", File.ReadAllBytes("Inter-Medium.ttf"), new OfficeFontFaceDescriptor(500));
drawing.AddText("Monthly report", 12, 12, 216, 56,
    new OfficeFontInfo("Inter", 20, new OfficeFontFaceDescriptor(500)));

Face matching follows CSS ordering within the requested family. Missing faces use the available matching face and existing fallback policy. Simulated bold paints its combined outline once, preserving the requested opacity.

Raster font selection checks glyph coverage before using its default font. When no selected font can paint the text, the managed stroke fallback keeps basic text and common list markers visible: •, ◆, ■, □, ❖, ➢ and ✔. Other unsupported characters use a placeholder. Register a font with the required glyphs when the document needs broader script coverage or exact typography.

Prepare scanned images

OfficeScanProcessor in OfficeIMO.Drawing prepares a separately owned raster for OCR. It supports explicit quarter-turns, manual straightening, confidence-filtered deskew, local paper-brightness normalization, black and white levels, gamma, grayscale or bilevel output, and proportional downsampling:

using OfficeIMO.Drawing;

OfficeScanProcessingResult processed = OfficeScanProcessor.Process(sourceImage,
    new OfficeScanProcessingOptions {
        Deskew = true,
        NormalizeBackground = true,
        ColorMode = OfficeScanColorMode.Grayscale,
        MaximumDimension = 3000
    }, cancellationToken);
OfficePoint sourcePoint = processed.Report.ProcessedToSource.TransformPoint(ocrPixelPoint);

The source image stays unchanged. The report records transformations, skipped decisions, estimated managed buffers, and a blank-page suggestion; pages are never removed. Pixel, buffer, and analysis-work limits throw OfficeScanProcessingLimitException, allowing the caller to retain the original. The operation does not detect quarter-turn orientation itself; an OCR provider or the caller supplies that evidence.

PNG optimal encoding preserves exact scan samples using grayscale, gray-alpha, RGB, RGBA, or palette layouts, including one-bit black/white output. It does not threshold or quantize the scan. Byte, stream, and buffer-writer APIs share the selection; read IHDR when inspecting samples. The raster-encoding section below describes color metadata and compression settings.

OfficeScanProcessor.CorrectPerspective(image, options) creates a rectangular raster from four normalized source corners. OfficeScanPerspectiveOptions requires a convex, clockwise quadrilateral inside the source image. The result includes a projective mapping in both directions so consumers can place recognized text back on the original. Perspective correction is separate from the affine transform reported by ordinary scan cleanup. Curved-page dewarping remains unsupported.

Quick start

Document lifecycle policy

using OfficeIMO;

var loadOptions = new DocumentLoadOptions {
    AccessMode = DocumentAccessMode.ReadOnly,
    PersistenceMode = DocumentPersistenceMode.Explicit
};

Word, Excel, and PowerPoint expose format-specific options derived from these shared contracts.

Shared operation and conversion results

OfficeIMO format packages keep their typed diagnostics while sharing a small result contract from OfficeIMO.Core:

using OfficeIMO;

IOfficeResult operation = result;
if (!operation.Succeeded) {
    // Inspect the concrete result for its typed failure details.
}

IOfficeResult<MyDocument> documentResult = result;
MyDocument document = documentResult.RequireValue();

IOfficeConversionResult<MyDocument, MyConversionReport> conversion = result;
MyDocument losslessDocument = conversion.RequireNoLoss();

IOfficeResult<T> standardizes Succeeded, Value, and RequireValue(). IOfficeConversionResult<TValue, TReport> also exposes Report, HasLoss, and RequireNoLoss(). Concrete results continue to expose format-specific diagnostics, warnings, or exceptions; the shared interface does not reduce them to an untyped error string.

Portable AES for restricted hosts

Browser WebAssembly and other hosts without synchronous platform AES can explicitly supply the dependency-free managed AES-CBC provider to a format API that accepts IOfficeAesCryptographyProvider. Desktop and server applications should normally keep using their native platform AES implementation.

using OfficeIMO.Security;

IOfficeAesCryptographyProvider aes = OfficeManagedAesCryptographyProvider.Default;

Colors and vector intent

using OfficeIMO.Drawing;

OfficeColor accent = OfficeColor.Parse("#336699");
OfficeColor printBlue = OfficeColorSpaceConverter.FromCmyk(1, 0.45, 0, 0.15);
OfficeImageFit fit = OfficeImageFit.Contain;

var badge = OfficeShape.RoundedRectangle(120, 32, 8);
badge.FillColor = OfficeColor.WhiteSmoke;
badge.StrokeColor = accent;
badge.Shadow = new OfficeShadow(OfficeColor.Black, 0.18, 3, 4);

Use OfficeIccColorProfile when an application already has embedded ICC profile bytes and needs the same bounded, dependency-free conversion used by OfficeIMO renderers:

using OfficeIMO.Drawing;

byte[] profileBytes = File.ReadAllBytes("display.icc");
if (OfficeIccColorProfile.TryCreate(profileBytes, out OfficeIccColorProfile? profile) &&
    profile.TryConvert(new[] { 0.25D, 0.5D, 0.75D }, out OfficeColor converted)) {
    Console.WriteLine(converted.ToHex());
}

if (profile?.HasOutputTransform == true &&
    profile.TryConvertToDevice(OfficeColor.CornflowerBlue, OfficeIccRenderingIntent.RelativeColorimetric, out double[] deviceColor) &&
    profile.TrySoftProof(OfficeColor.CornflowerBlue, OfficeIccRenderingIntent.RelativeColorimetric, out OfficeColor proofed)) {
    Console.WriteLine($"Device channels: {string.Join(", ", deviceColor)}; proof: {proofed.ToHex()}");
}

The managed contract accepts bounded RGB and Gray matrix/TRC input-device and display-device profiles plus RGB, CMYK, or three-to-eight-channel (3CLR–8CLR) LUT8 input transforms with a Lab profile connection space and LUT16 input transforms with an XYZ or Lab profile connection space. For LUT and ICC v4 mAB profiles, conversion selects A2B1 for relative or absolute colorimetric intent and A2B2 for saturation intent, falling back to A2B0 when the intent-specific transform is absent. It also accepts bounded ICC v4 RGB, CMYK, and 3CLR–8CLR A2B mAB input transforms, plus RGB/CMYK B2A mBA output transforms using the specification-defined curve, variable-grid CLUT, matrix, and offset combinations. Multichannel profiles support input conversion only. Output conversion is available through a valid B2A0 transform or the synthesized inverse of a supported RGB matrix/TRC profile; optional intent-specific tags fall back to B2A0 when that transform is present. TryCreate returns false for unsupported input profile classes and transform types, while malformed or unsupported optional output transforms leave HasOutputTransform false so the caller can choose an explicit color-management provider or fallback instead of receiving a silent approximation.

Image metadata and complete-content validation

OfficeImageReader exposes JpegComponentCount and TiffPhotometricInterpretation on OfficeImageInfo as nullable header evidence. The JPEG value is the frame's declared component count. The TIFF value is the raw PhotometricInterpretation tag from the first classic-TIFF or BigTIFF directory; missing, malformed or duplicate tags produce null. TIFF value 2 declares RGB and 5 declares separated samples. Three JPEG components alone do not prove RGB. Neither field establishes valid pixel data, an ICC profile or color-managed rendering. Manually constructed metadata and other image formats leave these fields null. Use the decoder and explicit profile APIs for their separate checks.

OfficeRasterImageDecoder preserves encoded color channels and applies supported image orientation. It does not automatically normalize embedded ICC profiles, PNG gamma, or chromaticities. Applications that require color-managed pixels must perform that conversion explicitly before using the decoded image. The profile APIs above provide bounded color conversion; metadata validation alone does not mean a raster image has been converted to sRGB.

For already unpacked, tightly packed 8-bit RGB, CMYK, or supported multichannel device samples, use the explicit raster converter with the corresponding embedded profile bytes:

OfficeIccRasterConversionStatus status = OfficeIccRasterConverter.TryConvertToSrgb(
    deviceSamples, width, height, profileBytes,
    new OfficeIccRasterConversionOptions {
        MaximumProfileBytes = 1024 * 1024,
        MaximumPixels = 1_000_000
    },
    out OfficeRasterImage? srgbImage);
if (status != OfficeIccRasterConversionStatus.Converted) {
    throw new InvalidDataException($"ICC raster conversion rejected the input: {status}");
}

The converter uses the shared ICC matrix/TRC and LUT engine. It rejects malformed or unsupported profiles and incomplete sample buffers, and accounts for the source, profile parser, and RGBA output before allocating pixels. The default ceilings are 4 MiB of ICC data, 4 million pixels, and 256 MiB of accounted managed memory; callers can lower them. The result is opaque sRGB pixels. Alpha, planar samples, higher bit depths, and extraction of device channels from encoded images are separate format concerns. The independent color corpus checks matrix RGB, ICC v4 LUT RGB, and CMYK LUT swatches against LittleCMS.

OfficeImageOptimizer still preserves or reports ICC metadata according to its metadata policy. It does not call the raster converter or claim that re-encoded pixels were normalized to sRGB.

Routed SVG filters on non-text shape/group content support SourceGraphic, SourceAlpha, preceding named results, Gaussian blur, offset, matrix color transforms, source-over composition and separable blend modes. These operations use managed RGBA buffers at one pixel per drawing unit, then retain a PNG in the scene. The default filter color space is linear RGB; a uniform sRGB declaration is also supported. The filter region clips every input/result and the final paint. Simple unrouted blur/offset/drop-shadow filters retain their existing vector approximation.

Managed filter graphs accept at most 32 primitives, blur deviation up to 64 drawing pixels per axis, and cumulative document work/intermediate-surface limits. Rotated or sheared graphs, explicit primitive subregions, mixed filter color spaces, unsupported primitives and graphs containing text, logical ActualText, links or pattern cells report unsupported features and preserve source geometry. Links on the filtered container retain their original geometry. Pass OfficeSvgDrawingReaderOptions.CancellationToken to cancel import and filter work.

SVG import loads embedded @font-face data through the bounded font loader into the drawing's scoped font collection. Font weight, stretch, slant and Unicode ranges participate in text fallback; remote and local font sources report unsupported features. CSS image-rendering retains nearest-neighbor sampling when a drawing is exported and reopened.

The SVG drawing reader supports a single rectangle, rounded rectangle, circle, ellipse, polygon, or path inside a userSpaceOnUse clip path, including transforms and even-odd filling. Compound clip unions, objectBoundingBox clips, and referenced or text clip geometry report unsupported features. Imported SVG text retains its full glyph paint inside an explicit root viewport clip. Text measurements determine layout, while the viewport and authored clip paths determine visible paint. Scene inspection should traverse drawing groups and effect groups to reach their text and shape content. Shape geometry crossing a nested SVG or symbol viewBox is retained until the viewport clip is applied. Local symbols without a viewBox retain their user coordinates and inherit paint from their use element. Symbol dimensions clip the content; they do not rescale it. Native import resolves omitted dimensions from the containing viewport. Caller-raster safety checks require explicit symbol width and height in documents with nested viewports or symbol references whose viewport context cannot be resolved by that check.

using OfficeIMO.Drawing;

OfficeImageInfo info = OfficeImageReader.Identify("logo.png");
Console.WriteLine($"{info.Width}x{info.Height} {info.MimeType}");

// Use content verification when an extension must not identify invalid bytes.
byte[] bytes = File.ReadAllBytes("upload.svg");
bool verified = OfficeImageReader.TryIdentifyByContent(bytes, "upload.svg", out OfficeImageInfo upload);

OfficeImageFit fit = OfficeImageFit.Contain;

TryIdentify(...) retains the metadata reader's extension fallback. TryIdentifyByContent(...) may use a file name to select the SVG parser, but succeeds only when the bytes match a supported format.

Text shaping and vertical text

Font resolution, glyph coverage, shaping diagnostics, and baseline placement are shared by the raster, SVG, and PDF drawing routes. Use OfficeDrawing.AddVerticalText(...) for top-to-bottom text. The optional OfficeIMO.Drawing.HarfBuzz package supplies full OpenType shaping and true vertical advances to raster outline rendering. SVG retains one searchable logical string and explicitly marks browser-native shaping with vertical writing attributes. PDF uses positioned embedded glyphs when the provider supplies complete vertical advances and logical coverage and the writer can align the glyph ink within its text box. Otherwise it retains searchable stacked text, reports a typed approximation, and strict conversion profiles reject that approximation. The dependency-free managed provider likewise reports when it cannot supply true vertical shaping.

EM QUAD (U+2001) and EM SPACE (U+2003) retain their font's mapped advances. When these scalars are missing, OfficeFontFaceCollection tries eligible registered fallback faces before synthesizing a blank advance of one em for supported TrueType or CFF fonts. Automatic fallback selection respects declared font ranges and complete-grapheme coverage. Measurement and outline cursor placement use the same advance. This missing-space fallback is limited to these two scalars.

Use TryValidateContent(...) at ingestion and export boundaries that must reject incomplete or corrupt image bodies. It applies the shared encoded-payload limit, validates the complete known container, decodes supported raster bodies, and validates every ICO entry. Both byte-array and stream overloads return the validated metadata; seekable streams are restored to their original position.

using System.IO;
using OfficeIMO.Drawing;

byte[] upload = File.ReadAllBytes("upload.png");
bool validBytes = OfficeImageReader.TryValidateContent(upload, "upload.png", out OfficeImageInfo byteInfo);

using Stream input = File.OpenRead("upload.png");
bool validStream = OfficeImageReader.TryValidateContent(input, "upload.png", out OfficeImageInfo streamInfo);

Bounded SVG safety checks

Use IsWithinSafetyLimits(...) before sending untrusted SVG to the ChartForgeX raster fallback. The predicate models the packaged ChartForgeX rasterizer's resource, reference, style, and work behavior; it is not a general safety approval for arbitrary SVG renderers:

using OfficeIMO.Drawing;

byte[] svg = File.ReadAllBytes("upload.svg");
var limits = new OfficeSvgDrawingReaderOptions {
    MaximumElements = 10_000,
    MaximumViewportDimension = 8_192,
    MaximumViewportPixels = 16 * 1024 * 1024
};

if (!OfficeSvgDrawingReader.IsWithinSafetyLimits(svg, limits)) {
    throw new InvalidDataException("The SVG exceeds the accepted safety profile.");
}

A true result means the payload is well-formed SVG and stays within the input, XML nesting, viewport, path-command, element, rendered-reference, rendered-payload, projected raster-paint, and filter-work ceilings enforced for the ChartForgeX fallback. It does not authorize network access or external resource loading by another renderer, and it does not mean every SVG feature can be projected into an OfficeDrawing. Apply renderer-specific resource and execution policies before using another SVG engine. TryRead(...) performs OfficeIMO's vector projection and reports unsupported features; use it when the drawing result is required.

MaximumElements, MaximumViewportDimension, and MaximumViewportPixels can be lowered for an application policy or raised for trusted input up to their documented hard maxima. They do not relax the fixed 8 MiB input, nesting, path-command, transform, reference-depth, conservative stylesheet/reference, or 256-viewport raster-work checks. Raster work includes projected paint bounds and the estimated cost of blur, morphology, convolution, and turbulence filter parameters.

Inspect and remove provenance carriers

OfficeIMO.Provenance inspects C2PA Content Credentials and IPTC Digital Source Type declarations without loading a cryptographic provider. The bounded parsers understand the format-native carriers used by JPEG, PNG, WebP, GIF, TIFF, SVG, ZIP-based document packages, and structured or variation-selector text.

using OfficeIMO.Provenance;

OfficeProvenanceReport report = OfficeProvenanceInspector.InspectFile("generated-image.png");
Console.WriteLine($"C2PA: {report.HasC2paManifest}");
Console.WriteLine($"Generative AI declaration: {report.HasGenerativeAiDeclaration}");

OfficeProvenanceRemovalResult removal = OfficeProvenanceRemover.RemoveFile(
    "generated-image.png",
    "clean-image.png");

foreach (OfficeProvenanceChange change in removal.Changes) {
    Console.WriteLine($"{change.Carrier}: {change.Location}");
}

Removal is selective. It removes structurally valid C2PA carriers and AI-specific trainedAlgorithmicMedia or compositeWithTrainedAlgorithmicMedia declarations while preserving unrelated metadata and non-AI source declarations. The generic Core API blocks signed ZIP packages because rewriting the package invalidates its signatures; callers that own the complete document save must handle signature invalidation separately.

Each valid Content Credentials record also carries a summary of what its active manifest claims: the generator, the recorded actions with their software agent and digital source type, ingredients, and the signer and issuer names from the signing certificate.

foreach (OfficeProvenanceEvidence evidence in report.Evidence) {
    if (evidence.Manifest is { } manifest) {
        Console.WriteLine($"{manifest.ClaimGenerator} · AI: {manifest.DeclaresGenerativeAi} · signed by {manifest.SignedBy}");
    }
}

The summary repeats the record's statements; it does not check them. Structural inspection does not claim that a manifest is authentic or trusted. Install OfficeIMO.Provenance.C2pa and use C2paToolProvenanceVerifier when content binding, signature mathematics, and certificate trust must be checked.

For an evidence-oriented result, combine structural carriers, optional cryptographic verification, exact Unicode findings, and vendor-specific detectors without collapsing them into an unreliable universal AI verdict:

OfficeProvenanceAssessmentReport assessment =
    OfficeProvenanceAssessment.InspectFile("article.md");

Console.WriteLine($"Verified credential: {assessment.HasVerifiedContentCredential}");
foreach (OfficeProvenanceSignalResult signal in assessment.ProviderSignals) {
    Console.WriteLine($"{signal.ProviderName}: {signal.Status}");
}

IOfficeProvenanceSignalDetector is deliberately provider-specific. A detector reports its own durable-media watermark, statistical-text watermark, visible disclosure, or deterministic artifact and keeps NotDetected, Inconclusive, ProviderUnavailable, and Error distinct. OfficeIMO does not turn the absence of one vendor's signal into “human-authored.”

Existing detector and verifier implementations remain valid. Providers that perform long-running work can additionally implement ICancellableOfficeProvenanceSignalDetector or ICancellableOfficeProvenanceVerifier; cancellation-aware assessment and workflow calls pass their token to those providers and fall back to the original contracts for compatibility.

Use OfficeTextIntegrityInspector to report exact invisible and context-sensitive Unicode code points. It reports offsets, code points, and risk; it does not call those characters an AI watermark. Format content-safety reports also mirror these as selectable NonPrintingUnicode findings when the owning adapter can verify and rewrite the exact native text node. Cleanup has no blanket mode: callers pass only reviewed finding IDs, so legitimate joiners, variation selectors, and typographic spaces are not silently normalized.

OfficeProvenanceAssessmentReport.TextIntegrityStatus, VerificationStatus, and ProviderSignalsStatus describe whether each check ran. A disabled or unsupported text check has no report; an absent provider is NotConfigured. Provider-specific result statuses retain their separate conclusions.

OfficeTextIntegrityReview binds occurrence selections to the exact source text and exports a separate copy without normalization:

var review = OfficeTextIntegrityReview.Inspect("invoice\u202E123 · language joiner: a\u200Db");
// After reviewing occurrence 0 (the directional override), remove only that occurrence.
byte[] copy = review.ExportSelected(review.Text, new[] { 0 });

For files, pass the encoded bytes to Inspect(bytes). Strict UTF-8 and BOM-declared UTF-16/32 are supported. Export retains the original encoding, BOM, line endings, and all unselected characters. RemoveSelected(currentText, indices) rejects changed source text and invalid indices; callers must inspect edits again. The review also exposes hashes of the decoded UTF-16 code units and, for file inputs, the original bytes.

Inspect concealed content before model ingestion

OfficeIMO.ContentSafety is separate from provenance. It reports native hidden text, white-on-white or otherwise low-contrast text, tiny or zero-size text, off-canvas/clipped content, notes/comments/alternative text, and exact Unicode evidence through the format package that understands the file. Concealment can be legitimate accessibility, review, layout, or metadata content; it is not an AI watermark or an authorship verdict.

OfficeContentInstructionDetector.Analyze(text) also provides advisory instruction signals for text consumers. It inspects at most one million source characters, 32 inline Base64 candidates and 32,768 decoded characters by default. Base64 inspection supports one layer of printable UTF-8 text and line-wrapped tokens; it never executes or returns decoded instructions. IsComplete reports budget coverage, not safety. Detect(text) returns the same signal identifiers for callers that only need a bounded heuristic list. Source text remains unchanged.

using OfficeIMO.ContentSafety;
using OfficeIMO.Word;

OfficeContentSafetyReport report = WordDocument.InspectContentSafety("candidate.docx");
foreach (OfficeContentSafetyFinding finding in report.Findings) {
    Console.WriteLine($"{finding.Risk}: {finding.Kind} at {finding.Location}");
}

OfficeContentSafetyFinding[] reviewed = report.Findings
    .Where(item => item.IsInstructionLike)
    .ToArray();

WordDocument.RemoveSelectedContent(
    "candidate.docx",
    "candidate.cleaned.docx",
    new OfficeContentCleanupSelection(reviewed.Select(item => item.Id)));

SVG uses the same report and selection contracts through the native drawing owner. The inspection resolves presentation attributes and the bounded stylesheet cascade, then uses the drawing renderer for clipping, compositing, paint-order, and background evidence. Visual comparison findings are report-only: a bounded native render cannot prove browser-equivalent shaping and paint. Exact cleanup remains available for supported source-local structural findings and reviewed Unicode code points. Text in reusable definitions, use or tref targets, conditional branches, unrecognized elements, and documents with scripts, event handlers, executable links, or animation also remains report-only because removing one source node can alter a different visible instance, renderer-selected branch, or runtime state.

using OfficeIMO.Drawing;

OfficeContentSafetyReport svgReport =
    OfficeSvgDrawingReader.InspectContentSafety("diagram.svg");

OfficeContentSafetyFinding[] concealedSvgText = svgReport.Findings
    .Where(item => item.CleanupCapability == OfficeContentCleanupCapability.RemoveText)
    .ToArray();

OfficeSvgDrawingReader.RemoveSelectedContent(
    "diagram.svg",
    "diagram.cleaned.svg",
    new OfficeContentCleanupSelection(concealedSvgText.Select(item => item.Id)));

Visual paint-order comparisons use explicit count and cumulative rendered-pixel budgets on OfficeSvgDrawingReaderOptions, plus fixed aggregate CSS-match and document-transformation ceilings. Callers can lower the public budgets for hostile-input services or set MaximumContentSafetyVisualComparisons to zero for structural-only inspection; the resulting report records that visual evidence was disabled. The safety inspection fails closed when declarations, values, selectors, at-rules, cascade keywords, or CSS work exceed the supported bounded subset, rather than using partial style evidence for removable findings. A changed UTF-8 cleanup result must also remain within the native SVG reader's hard input limit so reopen validation cannot be bypassed by encoding expansion.

Cleanup is always selection-based and stale-evidence checked. The adapter reopens and reinspects the rewritten artifact; it does not provide a blanket “remove anything unusual” switch. See the content safety support matrix for exact format coverage and renderer boundaries.

Transformations can make an existing Content Credential invalid. OfficeProvenanceLifecycle.FinalizeFile makes the disposition explicit:

var disposition = new OfficeProvenanceTransformationOptions {
    Policy = OfficeProvenanceTransformationPolicy.PreserveIfUnchanged
};

OfficeProvenanceTransformationResult audit = OfficeProvenanceLifecycle.FinalizeFile(
    sourcePath: "source.png",
    candidatePath: "resized.png",
    outputPath: "final.png",
    options: disposition);

The default blocks a changed credentialed source. Applications must explicitly select RemoveInvalidated for auditable carrier removal or SignAsDerived with an IOfficeProvenanceSigner, which records the source as the parent ingredient. Package-owned Word, Excel, PowerPoint, Visio, OpenDocument, EPUB, and PDF removal adapters remain the correct surfaces when package signatures also need safe disposition.

The provenance support matrix records the exact carriers, strict-removal behavior, verification boundary, and known limits.

Encode common raster formats

using OfficeIMO.Drawing;

var image = new OfficeRasterImage(320, 180, OfficeColor.White);
var options = new OfficeRasterEncodingOptions {
    Jpeg = new OfficeJpegEncodeOptions { Quality = 90 },
    Tiff = new OfficeTiffEncodeOptions {
        Compression = OfficeTiffCompression.Lzw,
        Predictor = OfficeTiffPredictor.Horizontal
    }
};

byte[] jpeg = OfficeRasterImageEncoder.Encode(image, OfficeImageExportFormat.Jpeg, options);
byte[] tiff = OfficeRasterImageEncoder.Encode(image, OfficeImageExportFormat.Tiff, options);
byte[] webp = OfficeRasterImageEncoder.Encode(image, OfficeImageExportFormat.Webp, options);

The same encoder can write directly to a caller-owned stream when materializing another complete output array would be wasteful. On .NET 8 and later, the codecs also accept IBufferWriter<byte> without adding a package dependency to OfficeIMO.Core:

using System.Buffers;

using (Stream output = File.Create("photo.webp")) {
    OfficeRasterImageEncoder.EncodeTo(image, OfficeImageExportFormat.Webp, output, options);
}

var writer = new ArrayBufferWriter<byte>();
OfficeRasterImageEncoder.EncodeTo(image, OfficeImageExportFormat.Png, writer, options);
ReadOnlyMemory<byte> png = writer.WrittenMemory;

The stream overload leaves the destination open. PNG and lossless WebP default to OfficeImageEncodingEffort.Balanced. Select effort on the existing format options; document conversion options expose the same settings through RasterEncoding:

var exportOptions = new OfficeImageExportOptions();
exportOptions.RasterEncoding.Png.Effort = OfficeImageEncodingEffort.Smallest;
exportOptions.RasterEncoding.Webp.Effort = OfficeImageEncodingEffort.Smallest;

// The same settings work with direct raster encoding.
options.Png.Effort = OfficeImageEncodingEffort.Fast;
byte[] fastPng = OfficeRasterImageEncoder.Encode(image, OfficeImageExportFormat.Png, options);

Fast uses cheap heuristics and a single compression plan. Balanced retains useful compression without competing full-image trials. Smallest opts into the more expensive searches described below. All levels preserve every decoded sample, including hidden RGB under transparent pixels, and are deterministic for the same input, encoder build, and runtime. Effort controls compression work rather than image quality; it is not a strict size ordering for every image.

PNG chooses exact grayscale (including 1-, 2-, and 4-bit samples), grayscale-alpha, RGB, RGBA, or a palette of up to 256 RGBA colors. Fast and Balanced choose the layout by packed sample storage plus palette overhead, score a bounded sample of each row to select a PNG filter, and compress once. Fast uses the platform's fastest Deflate level; Balanced uses Optimal. Smallest compares compressed direct/palette layouts and adaptive/unfiltered rows, scores all five filters across each row, and uses smallest-size Deflate on .NET 8/10 (Optimal on older targets). Scratch remains row-sized, without a full filtered image.

Effort applies when OfficePngEncodeOptions.Compression is Optimal. Explicit Fastest compression retains unfiltered RGBA and fastest Deflate; Stored retains uncompressed zlib blocks. A supplied grayscale ICC profile still requires grayscale samples. PNG raster exports declare sRGB, gamma, and chromaticities; EncodeWithMetadata writes a supplied ICC profile instead and retains a compatible sample layout.

OfficePngWriter.EncodeRgba64(width, height, ushortRgba, options) preserves sixteen-bit source samples in grayscale, grayscale-alpha, RGB or RGBA output. It supports the same effort levels with Compression = Optimal. The shared decoded OfficeRasterImage model remains eight-bit.

TIFF output supports uncompressed, LZW, PackBits, and Deflate RGBA strips; LZW and Deflate use horizontal prediction by default. OfficeTiffCompression.CcittGroup4 writes one-bit T.6 strips from opaque exact black/white pixels and rejects other input. It applies to single images, streams, and OfficeTiffCodec.EncodePages(...). Use OfficeRasterImageEncoder.EncodeWithMetadata to attach ICC data through the shared metadata owner.

JPEG defaults to quality 85, 4:4:4 chroma, sequential scans, and optimized Huffman tables. Huffman optimization changes entropy coding without changing decoded pixels, but retains coefficients while measuring symbol frequencies. Set OptimizeHuffman = false for standard tables and lower coefficient memory, Progressive = true for progressive scans, or Subsampling = OfficeJpegSubsampling.Y420 for smaller output with reduced chroma resolution. JFIF carries DpiX/DpiY; OfficeJpegMetadata and the shared metadata-aware encoder carry ICC APP2 segments.

TIFF DpiX and DpiY are physical DPI. To retain native centimeter density or a unitless aspect ratio, assign OfficeTiffEncodeOptions.Resolution = new OfficeImageResolution(horizontal, vertical, unit). That immutable override applies to every encoded page and to streaming output. Meter values normalize to centimeters. The shared encoder retains the override until an explicitly assigned shared DpiX or DpiY selects physical DPI instead; metadata.Resolution supplies a snapshot directly from OfficeImageMetadata.

The shared encoder also writes BMP, binary PBM, TGA, and ICO with OfficeImageExportFormat.Bmp, Pbm, Tga, and Icon. BMP uses explicit RGBA masks and TGA writes a straight-alpha 32-bit image. PBM composites transparency over white and thresholds luminance into a one-bit image. ICO contains a PNG entry; OfficeIconEncoder.Encode(images, options, cancellationToken) accepts several images for a multi-resolution icon, with each image limited to 256 pixels on either axis.

Managed decoding accepts ASCII and binary PBM/PGM/PPM, including sixteen-bit PGM/PPM samples; indexed, grayscale, true-color, and RLE TGA; and PNG or conventional DIB icon entries. Select an ICO entry through OfficeRasterDecodeOptions.FrameIndex. Container inspection checks entry dimensions before allocating pixel buffers.

Portable SVG images and text

SVG keeps non-interpolated images as embedded rasters with pixelated sampling hints. Crop, placement, opacity, and source-decoder policy stay with the shared Drawing pipeline. Pixel dimensions use the raster rounding rule; mask transfer tables and text outline strokes retain their paint semantics.

Use registered font bytes and SvgTextAsPaths when eligible text should travel as outlines:

var drawing = new OfficeDrawing(400, 100);
drawing.Fonts.Add("Body", File.ReadAllBytes("Body.ttf"));
drawing.AddText("Portable SVG", 8, 8, 384, 80,
    new OfficeFontInfo("Body", 24), wrapText: false);
var svg = drawing.ExportImage(OfficeImageExportFormat.Svg,
    new OfficeImageExportOptions { SvgTextAsPaths = true });
File.WriteAllBytes("preview.svg", svg.Bytes);

The option supports single-line horizontal ASCII text with one resolved outline face. Wrapped, decorated, vertical, complex-script, color-glyph, and mixed-font fallback runs retain <text> and their required fonts. Supported already-painted runs use outlines automatically. Outlines retain logical accessibility labels, but are not selectable SVG text. Fonts unused by retained text are omitted; retained font programs are not subsetted. Independent SVG and raster text placement and antialiasing can still differ.

Choose lossless or lossy WebP

WebP output defaults to lossless VP8L. Fast uses one raw statistical Huffman/LZ77/color-cache plan with a shallow match search. Balanced samples color diversity and predictor residuals to decide whether a short block-predictor search is worthwhile, considers subtract-green, and builds one main compression plan with a bounded match search. Smallest compares raw and predicted plans, all supported block predictors, cross-color transforms and cache choices. Both byte-array and stream encoders retain one pixel workspace and bounded match chains instead of a full token list. Inputs above four million pixels, or candidates whose workspace cannot fit the managed memory limit, retain exact literal output at every effort level. Effort applies only to lossless VP8L; explicit VP8 lossy output uses Quality separately.

var webpOptions = new OfficeRasterEncodingOptions {
    Webp = new OfficeWebpEncodeOptions {
        Mode = OfficeWebpEncodingMode.Lossy,
        Quality = 85,
        WritePhysicalResolution = true
    }
};

using Stream webpOutput = File.Create("photo.webp");
OfficeRasterImageEncoder.EncodeTo(
    image, OfficeImageExportFormat.Webp, webpOutput, webpOptions);

Lossy quality ranges from 1 through 100 and defaults to 85. VP8 uses 4:2:0 chroma sampling, so quality 100 still changes color samples; alpha remains exact at every quality. Transparent output stores filtered, uncompressed alpha. Lossy images accept dimensions up to 16,383 pixels on either axis; lossless VP8L accepts 16,384. Invalid quality or unsupported dimensions fail explicitly. Both modes support cancellation and the shared encoded-byte and managed working-memory limits. The encoder writes one static image; animated WebP encoding is outside its contract.

OfficeWebpCodec.Encode and EncodeTo accept the same OfficeWebpEncodeOptions directly. Direct codec options omit physical resolution metadata by default. Set WritePhysicalResolution to true and supply DpiX and DpiY to include EXIF density; format-neutral encoding applies explicitly assigned shared DPI values ahead of the WebP-specific values.

Inspect and select frames or pages

OfficeRasterContainerInspector exposes one bounded inventory for static images, GIF and APNG frames, TIFF pages, and WebP frame metadata. The inventory includes canvas size, count, timing, offsets, disposal, blending, loop count, and the backwards-compatible default image. Decode accepts either bytes or a readable stream; a seekable stream is restored to its original position.

using var input = File.OpenRead("pages.tiff");
var decodeOptions = new OfficeRasterDecodeOptions {
    FrameIndex = 1,
    FrameLossPolicy = OfficeRasterFrameLossPolicy.UseSelectedFrame,
    MaximumEncodedBytes = 32 * 1024 * 1024,
    MaximumDecodedPixels = 20_000_000,
    CancellationToken = cancellationToken
};

if (OfficeRasterImageDecoder.TryDecode(input, decodeOptions, out var page, out var decodeInfo)) {
    Console.WriteLine($"Selected {decodeInfo.SelectedFrameIndex + 1} of {decodeInfo.FrameCount}");
}

Set FrameLossPolicy to RejectMultipleFrames when a static result must not discard animation frames or document pages. Animated WebP pixel composition remains a caller-codec boundary, but its frame inventory is still available for a fail-closed decision.

The managed JPEG decoder supports eight-bit baseline and eight/twelve-bit extended sequential and progressive DCT, plus two-through-sixteen-bit Huffman and arithmetic lossless JPEG. Lossless decoding supports all seven predictors, point transforms and row-aligned restart intervals. A nonzero point transform discards source low bits and restores them as zero. Standalone decoding projects to the public eight-bit raster buffer. Lossless JPEG-compressed TIFF retains native two-through-sixteen-bit samples through alpha and color conversion. YCbCr conversion uses the native sample midpoint before eight-bit projection. TIFF retains fractional RGB through alpha unassociation and ICC conversion instead of rounding it back to the source bit depth. Sequential and progressive arithmetic JPEG support eight/twelve-bit samples, conditioning tables and restart intervals. Progressive decoding accepts spectral bands, successive approximation and DC-only previews, retaining missing coefficients as zero. Lossless arithmetic uses two-dimensional difference conditioning and bounded row history; grayscale/RGB fixtures cover all precisions and predictors. DCT JPEG-compressed TIFF retains native eight/twelve-bit samples; packed twelve-bit TIFF supports byte-aligned rows with Predictor 1.

The managed JPEG XR decoder accepts single-image tagged .jxr, .wdp, and .hdp containers with unsigned eight-bit gray/RGB/BGR/BGRA, unsigned sixteen-bit gray/RGB/RGBA, and finite sixteen/thirty-two-bit fixed-point or floating-point gray/RGB/RGBA pixels. It handles 4:4:4, 4:2:2, and 4:2:0 chroma with defined sampling-grid centering, spatial and frequency packet order, lossless and lossy quantization, all three overlap modes, hard and soft tiles, omitted high-frequency bands, trimmed flexbits, and interleaved or separate alpha. Premultiplied alpha becomes straight RGBA at source precision before samples are rounded to the public eight-bit buffer. Fixed-point and floating-point samples default to linear scRGB and use the shared sRGB conversion; out-of-range colors are clipped to the SDR output gamut. The container orientation is applied. Encoded bytes, padded coefficient/sample buffers, output, retained caller data, and cancellation share the Core resource limits. Embedded ICC data is available to the color-management layer; ordinary unsigned raster decoding returns device channel values. The color-management layer also decodes unsigned eight/sixteen-bit CMYK and CMYKDirect with a supplied four-component ICC profile, preserving source precision and alpha. Unprofiled CMYK does not produce approximate RGB pixels. Three-to-eight-channel unsigned eight/sixteen-bit images also require a matching ICC profile. Interleaved alpha can contain the same or fewer frequency bands than the primary plane. Non-finite samples and multiple image directories are outside this decoder contract.

Read and edit portable image metadata

OfficeImageMetadata owns typed Exif values and raw XMP, ICC, and IPTC IIM profile bytes. Its arrays and snapshots own their storage. Standard tags have descriptive names; application-defined tags use their numeric identifier, TIFF representation, and directory. Scalars and arrays use .NET numeric types or OfficeRational / OfficeSignedRational.

byte[] original = File.ReadAllBytes("photo.jpg");
OfficeImageMetadata metadata = OfficeImageMetadata.Read(original);
metadata.SetExifValue(OfficeExifTag.Software, "Photo workflow");
metadata.SetExifValue(OfficeExifTag.ExposureTime, new OfficeRational(1, 125));
metadata.RemoveExifValue(OfficeExifTag.GPSLatitude);
metadata.RemoveExifValue(OfficeExifTag.GPSLongitude);
OfficeImageFileWriter.WriteAllBytes("annotated.jpg", OfficeImageMetadata.Apply(original, metadata));

OfficeImageMetadataRemovalResult removal = OfficeImageMetadata.Remove(
    original, OfficeImageMetadataProfileKinds.Exif | OfficeImageMetadataProfileKinds.Xmp);
Console.WriteLine($"Found {removal.PresentProfiles}; removed {removal.RemovedProfiles}");
File.WriteAllBytes("private.jpg", removal.EncodedBytes);

Metadata replacement preserves JPEG entropy data, PNG image chunks, WebP image payloads, and TIFF raster encoding fields. TIFF replacement edits the primary image and retains page links; profile removal visits every page. Clearing or removing TIFF Exif retains each page's rendering orientation because changing it would change displayed pixels. The remaining orientation is exposed by GetExifValue and can make HasExifProfile true; use an explicit SetExifValue or RemoveExifValue to change that rendering field. TIFF metadata operations limit the aggregate strip, tile, and JPEG-interchange pixel references across reachable directories to 65,535. The alias check includes child directories and rejects edits that would erase pixel data. GIF supports XMP and ICC application profiles, while BMP supports V5 ICC profiles and physical density. Removing profile families from PBM/PGM/PPM or TGA returns the validated image unchanged because those formats have no defined carriers for these profile families. Unsupported carriers fail explicitly. ICC metadata edits preserve profile bytes; they do not transform pixel colors.

OfficeImageMetadata validates complete BMP storage before reading or editing it, including palettes, row data, RLE commands, embedded JPEG/PNG payloads, and V5 color-profile ranges. Metadata operations accept supported BMP storage layouts independently of the managed pixel decoder's narrower format support. TIFF IPTC writes use byte-typed fields to preserve exact profile lengths and trailing zero bytes.

Resolution values retain native aspect-ratio, pixels-per-inch, pixels-per-centimeter, or pixels-per-meter units when the container can represent them. PhysicalDpiX and PhysicalDpiY convert physical units to DPI and return null for an aspect ratio. JPEG, WebP, and TIFF normalize meter-based density to centimeters. TIFF encoder overloads preserve native units and aspect ratios unless the caller explicitly sets shared DPI. TIFF metadata edits retain exact rational density fields when the effective native values are unchanged. Authored TIFF/Exif rational density remains positive and accurate within one part in a trillion, or is rejected before output; its encoded numerator and denominator may differ from earlier output. JPEG replacement updates JFIF density, creates that carrier when absent, and synchronizes density in an existing Exif profile. GIF has an aspect-ratio field and no physical-density field: lossless edits require AspectRatio, while PrepareForEncoding projects resolution to that unit for GIF. Its ratio is rounded to the GIF field's representable increments. TIFF-relative opaque maker notes require the original TIFF container for lossless preservation; RequiresOriginalTiffContainer reports that constraint. Exporting those notes to another container requires explicitly removing or replacing the note. Superseded Exif values are erased when their storage is exclusive; aliased ranges that would erase another field or image pixels are rejected.

Use ParseExifProfile and EncodeExifProfile for metadata-only workflows. They accept and return classic TIFF Exif bytes without JPEG framing, and parsing also accepts the JPEG Exif prefix. Parsed TIFF fields preserve their original representations, including NUL-separated ASCII strings and zero-count arrays, through unrelated edits and profile export. SetExifValue validates new edits and requires ASCII text without embedded NULs and nonempty typed arrays. Both profile methods have cancellation-token overloads. Clone preserves independent edits and the original-container constraints. Metadata rewriting bounds retained inputs, profiles, stream backing growth, and the final encoded array against the Core managed working-set limit. Malformed ICC profiles are rejected before replacement. C2PA removal uses the canonical carrier inspector and retains malformed or conflicting carriers rather than treating unrelated bytes as a removable manifest.

When re-encoding pixels into another format, call PrepareForEncoding(destinationFormat, out omittedProfiles) before Apply. The returned copy contains supported profile families and reports what the destination cannot carry. For example, JPEG IPTC IIM metadata is omitted when producing PNG, while Exif, XMP, and ICC are retained. This explicit projection leaves the source metadata intact; lossless replacement still rejects unsupported profiles.

Inspect source metadata and normalized values

OfficeImageMetadata.Inspect returns passive metadata evidence without editing the image or decoding pixels. It retains Exif field identities, original counts, profile-relative directory/entry/value offsets and duplicate tags. Values omitted by a retention limit keep their descriptors. InspectExifProfile provides the same evidence for standalone classic TIFF Exif bytes, including the optional JPEG Exif prefix.

OfficeImageMetadataInspection inspection = OfficeImageMetadata.Inspect(File.ReadAllBytes("photo.jpg"));
foreach (OfficeExifTimestamp timestamp in inspection.Timestamps) {
    Console.WriteLine($"{timestamp.FieldName}: {timestamp.RawValue}; explicit timezone: {timestamp.HasKnownTimezone}");
}
if (inspection.GpsCoordinate is OfficeExifGpsCoordinate coordinate) {
    Console.WriteLine($"{coordinate.Latitude}, {coordinate.Longitude}");
}
Console.WriteLine($"Authored density: {inspection.Resolution.IsDeclared}");

Capture timestamps retain their raw date, offset and subseconds. A date without an offset, or with Exif's blank unknown-offset representation, remains a local wall-clock value with an unknown timezone; no machine timezone is assigned. The timestamp companions use the tag identities and unknown-value forms in the CIPA Exif specification. Fractions beyond .NET's seven-digit precision retain their source text and report truncation. GPS coordinates are derived only from complete, valid DMS rationals and cardinal references in one profile. Invalid or conflicting density carriers return no effective authored resolution, and absent density is never reported as an embedded 96-DPI value.

XMP values retain namespace URIs, RDF sequence/bag/alternative identity, item order, language and resource qualifiers. The inspector uses the shared secure XML loader and the canonical digest-bound JPEG extended-XMP assembler. Arbitrary structured RDF remains an explicit omission. OfficeXmpInspector.Inspect also accepts standalone packets. OfficeIccProfileInspector.Inspect separates header/tag-range structural validity from support by the managed input-transform engine; a valid unsupported profile is not labeled corrupt.

OfficeImageMetadataInspectionOptions bounds input, profiles, retained Exif descriptors/values, XML nodes and retained XMP characters, and ICC tag descriptors. Defaults are 16 MiB of encoded input, 4 MiB per profile, 512 Exif fields, 64 KiB per Exif value, 4 MiB of retained Exif value bytes, 4,096 XML nodes, 512 XMP values and one million retained XMP characters. Diagnostics identify malformed, ambiguous, omitted and unsupported evidence. Classic Exif inspection covers the primary image, camera, GPS and interoperability directories. Additional TIFF pages/private IFD trees, proprietary MakerNote interpretation and BigTIFF profile parsing remain outside this inspection profile. Structural metadata evidence does not establish pixel integrity, authorship or cryptographic provenance.

The default XMP value ceiling is 4,096 characters and the retained ICC descriptor ceiling is 256 tags. Options are snapshotted and require finite positive values within their supported ceilings. An unreadable/unsupported image container or an exceeded encoded-input limit fails the operation; cancellation propagates. Individual profile and value-retention omissions appear in Issues, and oversized Exif values can retain only their source descriptors. Check those issues before treating the retained metadata as complete.

Inspect and edit HEIF container metadata

OfficeHeifMetadataReader reads HEIF/HEIC brands, primary image dimensions and transforms, item properties, locations, and references. Its EXIF and XMP methods read each metadata family independently without decoding image pixels:

using OfficeIMO.Drawing;

if (OfficeHeifMetadataReader.TryReadInfo("photo.heic", out OfficeHeifImageInfo? info)) {
    Console.WriteLine($"{info!.Width}x{info.Height}; EXIF: {info.HasExif}");
}

if (OfficeHeifMetadataReader.TryReadExifProfile("photo.heic", out OfficeImageMetadata? exif) &&
    exif != null) {
    exif.SetExifValue(OfficeExifTag.Software, "Photo workflow");
    bool saved = OfficeHeifMetadataReader.TryWriteExifProfile("photo.heic", "annotated.heic", exif);
}

Metadata reads and writes select the unique cdsc item associated with the pitm primary image, regardless of item declaration order. A sole unassociated item remains supported for older containers. Multiple possible items, or an item associated only with another image, are not selected. TryReadInfo retains every declared item; ExifItem and XmpItem identify the selected items or are null when selection is ambiguous. Truncated item, reference, location, or property-association collections return false without partial information.

Byte-array overloads return independently owned output. Stream readers start at the current position, restore seekable streams, and leave them open. HasExifItem and HasXmpItem report declared items even when no readable payload is located. The writer replaces or clears an existing single absolute extent within an mdat payload; null metadata clears the item. It does not create missing items or rewrite item-data-box, external, derived, or multi-extent storage. Shared extents are rejected so clearing old metadata cannot erase image pixels or another item. Replacement bytes occupy a framed mdat box; unchanged sibling payloads are preserved.

Protected items and XMP items with a nonempty MIME content-encoding declaration remain visible through the info and presence APIs. Their payload reads and writes, including clearing, return false; independent edits to another family leave their bytes unchanged. Core does not provide a HEIF decryption or content-decoding fallback.

XMP reads reject malformed UTF-8, and XMP writes reject strings containing unpaired UTF-16 surrogates. Independent EXIF edits preserve an unreadable XMP sibling as bytes. A valid new XMP packet can replace a malformed existing packet through the direct writer API.

Input and output are bounded to 128 MiB, individual metadata/property payloads to 16 MiB, declared item collections to 4,096 entries, and parser work to 65,536 records. Operations include known caller stream backing and supplied XMP strings in the managed working-set budget and observe cancellation during parsing and preparation. Rejection or cancellation during preparation leaves source bytes and an existing output file untouched. File writers stage the complete output beside the destination and atomically replace it, so a failed staging write preserves the existing file. This metadata API does not provide HEIF pixel decoding or encoding.

Save encoded image files atomically

Use OfficeImageFileWriter.WriteAllBytes(path, encodedImage, cancellationToken) or WriteAllBytesAsync to save completed encoded bytes. Both stage output in the destination directory, then create or atomically replace the final file. They reuse Core's shared file commit owner and fail explicitly if atomic replacement is unsupported. Staging failures and cancellation observed before commit preserve an existing destination; failed staging files are removed when the filesystem permits cleanup.

The byte array is borrowed without cloning or parsing. Keep it unchanged until the method or returned task completes. The writer observes cancellation during staging and immediately before commit. Once final replacement begins, it can finish even if cancellation is requested. These methods save already encoded bytes; they do not validate image formats or promise power-loss durability.

Optimize encoded images for a placement

OfficeImageOptimizer resizes and re-encodes a static raster image for the pixel bounds where it will be used:

using OfficeIMO.Drawing;

byte[] source = File.ReadAllBytes("photo.jpg");
var request = new OfficeImageOptimizationRequest(1200, 800) {
    OutputFormat = OfficeImageFormat.Jpeg,
    JpegQuality = 82,
    JpegSubsampling = OfficeJpegSubsampling.Y420,
    JpegProgressive = true,
    JpegOptimizeHuffman = true,
    ResamplingMode = OfficeRasterResamplingMode.Lanczos3,
    ResamplingColorSpace = OfficeRasterResamplingColorSpace.LinearLight,
    MetadataPolicy = OfficeImageMetadataPolicy.SelectiveCopy,
    MetadataSelection = OfficeImageMetadataKinds.Exif |
                        OfficeImageMetadataKinds.Xmp |
                        OfficeImageMetadataKinds.Icc |
                        OfficeImageMetadataKinds.Orientation |
                        OfficeImageMetadataKinds.Resolution
};

OfficeImageOptimizationResult result = OfficeImageOptimizer.Optimize(source, request, "photo.jpg");
File.WriteAllBytes("photo.optimized.jpg", result.Bytes);
Console.WriteLine($"{result.Status}: {result.BytesSaved} bytes saved");
Console.WriteLine($"Metadata lost: {result.Metadata.Lost}");

The request preserves aspect ratio, avoids upscaling, keeps the original when re-encoding is not smaller, and retains source DPI by default. ResamplingMode defaults to premultiplied-alpha bilinear sampling; choose Area for coverage-correct downsampling or Lanczos3 for a sharper high-quality filter. Filtering remains in encoded sRGB by default. Select LinearLight when the workload benefits from physically linear color averaging and accepts its additional cost. Set OutputDpiX and OutputDpiY to override density. Output can be PNG, JPEG, TIFF, or WebP; use PngCompression, the JPEG quality/subsampling/progressive/Huffman settings, or TiffCompression and TiffPredictor for the selected format.

MetadataPolicy can preserve, strip, or selectively copy EXIF, XMP, ICC, orientation, comments, and resolution categories. A JPEG-to-JPEG rewrite preserves selected EXIF, standard single-packet XMP, and ICC bytes, applies embedded orientation to pixels, and neutralizes the copied orientation value. Adobe extended XMP is not copied during re-encoding; when selected XMP includes extension segments, the result reports XMP in Lost. The result reports PolicyApplied, Preserved, Normalized, Stripped, and Lost; unsupported or undecodable input returns the original bytes with PolicyApplied = false, and a required strip or selective-copy rewrite is never replaced by the metadata-bearing original merely because it is smaller. Metadata that has no safe output carrier is reported as loss rather than silently claimed as preserved; OfficeIMO does not currently perform ICC color conversion. Animated and multi-page input is rejected so optimization never silently drops frames or pages.

OfficeRasterExportPlanner is the shared pre-allocation owner for image export. It combines the caller's MaximumRasterPixels with renderer and encoder dimension/pixel limits, then either reduces scale with IMAGE_RASTER_SCALE_REDUCED or throws OfficeImageExportLimitException, according to RasterOverflowBehavior. The returned plan also owns the effective encoding settings: CreateEncodingOptions() reduces encoded density with the raster scale so safety limits preserve the document's physical size. Explicit top-level DpiX/DpiY values apply across formats; when those values are not assigned, format-specific PNG, JPEG, and TIFF density remains authoritative. Drawing's managed PNG and APNG, JPEG, classic unsigned 8/12/16-bit or finite floating 16/24/32/64-bit grayscale/RGB/RGBA/device-CMYK TIFF and 8-bit palette TIFF, uncompressed BMP, composited GIF, and ordinary lossless VP8L and lossy VP8 WebP paths (including raw or lossless-compressed separate alpha planes) enforce encoded-payload and decoded-pixel guards. TIFF accepts both byte orders, chunky or planar strips and tiles with uncompressed, LZW, PackBits, or Deflate payloads and integer or floating-point horizontal prediction; sample and associated-alpha precision is retained until eight-bit RGBA output or ICC conversion. Packed twelve-bit samples require Predictor 1. Mixed component widths and reversed bit order outside CCITT are rejected; arbitrary page selection and bounded multi-page writing use the same page contract. Floating TIFF values are normalized device components, with explicit ICC conversion when supplied and clipping to the SDR range at output; they do not implicitly declare linear scRGB or request automatic scientific-data rescaling. Non-finite color or alpha samples are rejected; unspecified extra channels and tile padding are ignored. TIFF also supports packed grayscale/palette samples, CCITT T.4/T.6 including the optional uncompressed mode, and baseline eight-bit, Huffman/arithmetic extended-sequential eight/twelve-bit or Huffman/arithmetic lossless two-through-sixteen-bit JPEG payloads. Bounded legacy compression-6 interchange images and table-pointer scans retain the eight/twelve/sixteen-bit contract; see the TIFF support and qualification boundaries. BigTIFF, animated WebP pixel decoding, and unsupported JPEG-in-TIFF processes remain caller-codec boundaries. OfficeRasterImageFallbackCodec can wrap an application codec at the final raster boundary. It reports IMAGE_SOURCE_DECODED_BY_CALLER_CODEC when that codec succeeds; if neither Drawing nor the application can decode a source image, it returns a visible placeholder and IMAGE_SOURCE_DECODE_FALLBACK instead of allowing the renderer to omit the image silently.

Every format package builds on the same fluent export contract. FitWithin(width, height), FitWithinWidth(...), and FitWithinHeight(...) cap both raster and SVG output without enlarging smaller content. ConfigureOptions(...) exposes the complete provider-specific option object when no dedicated fluent shortcut exists. Batch limits, cancellation, progress, and WithRenderTimeout(...) apply to the complete operation, including streaming saves. Each batch result reports its zero-based SequenceIndex; SequenceCount is populated when the total is known before streaming or after a fluent builder materializes the complete result list.

For raster complex text, set TextShapingProvider and optionally TextShapingLanguage on any shared image-export options or use WithTextShaping(...) on a fluent builder. OfficeManagedTextShapingProvider.Instance is the dependency-light built-in provider for a bounded Arabic-script subset (core and extended Persian/Urdu letters), with TrueType and OpenType CFF font loading. Drawing passes the selected font bytes, collection face, variation coordinates, base direction, language, cancellation token, and Unicode source mapping to any provider, then caches the resolved run for measurement and painting. Retained Drawing text keeps the selected program and glyph positions when divided between complete logical source clusters. Mark attachment, required lam-alef ligatures, and broader GSUB/GPOS behavior require a complete shaping provider. If no provider accepts a complex run, the managed Arabic-script and bounded bidirectional fallback keeps common text visible and adds IMAGE_TEXT_SHAPING_FALLBACK as an approximation. This fallback preserves per-letter joining without claiming complete mark or required-ligature shaping. Set Policy.RequireNoLoss = true when that fallback is not acceptable.

Deterministic text measurement

using OfficeIMO.Drawing;

var measurer = OfficeTextMeasurer.Create();
var style = measurer.CreateStyle(new OfficeFontInfo("Aptos", 11, OfficeFontStyle.Regular));
OfficeTextMetrics metrics = measurer.Measure("Quarterly report", style);

if (metrics.WidthPixels > 240) {
    Console.WriteLine("The label needs wrapping or a smaller font.");
}

Language-pattern hyphenation

OfficeTextHyphenationPatterns.GetBreakpoints(token, language) returns optional UTF-16 breaks in the original word using embedded US English (en-US, alias en) or reformed German (de-DE, aliases de, de-1996, de-DE-1996) resources. It preserves case, surrounding punctuation and Unicode source offsets. The resources enforce two letters before a break, and three after it for US English or two for German. Empty/unsupported tags, internal punctuation, digits and tokens longer than 512 UTF-16 code units produce no automatic breaks. Other regional English and German spelling tags are unsupported.

using OfficeIMO.Drawing;

IReadOnlyList<int> breaks = OfficeTextHyphenationPatterns.GetBreakpoints("representation", "en-US");
// 3, 5, 8, 10; the caller decides which permitted break fits the line.

Renderers can use the same Core result through their existing hyphenation callbacks. The explicit OfficeTextHyphenationLexicon remains available for application-owned dictionaries. Embedded resources are versioned in Typography/Hyphenation/manifest.json; their copyright and permission notices are retained in the third-party notices.

Reusable ink

using OfficeIMO.Drawing;

var stroke = new OfficeInkStroke {
    Color = OfficeColor.Crimson,
    Width = 2.2,
    Height = 2.2,
    Bias = OfficeInkBias.Handwriting,
    RecognizedText = "hello"
};
stroke.AddPoint(8, 30, 0.35)
      .AddPoint(60, 12, 1.0)
      .AddPoint(120, 36, 0.55);

var ink = new OfficeInkDocument().Add(stroke);
OfficeDrawing inkDrawing = OfficeInkRenderer.Render(ink, width: 140, height: 60);

The model keeps sampled geometry, normalized pressure, pen dimensions and tip shape, transforms, handwriting/drawing bias, language, and recognition alternatives. A format adapter decides how those values map to native storage.

Structured math

using OfficeIMO.Drawing;

OfficeMathExpression expression = OfficeMath.Fraction(
    OfficeMath.Row(
        OfficeMath.Identifier("x"),
        OfficeMath.Operator("+"),
        OfficeMath.Number("1")),
    OfficeMath.Radical(OfficeMath.Identifier("y")));

string mathMl = OfficeMathMarkup.ToMathMl(expression);
string latex = OfficeMathMarkup.ToLatex(expression);
OfficeDrawing mathDrawing = OfficeMathRenderer.Render(expression);

Supply a math font when equations need its OpenType MATH spacing, glyph variants, or stretch assemblies:

var mathOptions = new OfficeMathRenderOptions {
    Font = new OfficeFontInfo("Document Math", 24D),
    Dpi = 144D
};
mathOptions.Fonts.Add("Document Math", File.ReadAllBytes("document-math.otf"));
OfficeDrawing equation = OfficeMathRenderer.Render(expression, mathOptions);

UseFontMathMetrics is enabled by default. Available MATH constants, italic corrections, accent positions, math kerning, variants and assemblies guide layout; missing data uses the existing geometric fallback. Set the option to false to use the caller's script and rule settings. Font size is in points and Dpi controls drawing density. Optical-size selection and font tracking use the authored point size for both measurement and paint. Fonts are caller supplied; OfficeIMO.Core does not ship a math font or require a native rendering engine.

MathML mo tokens retain explicit prefix, infix, or postfix form and independent lspace/rspace values in em, px, %, or unitless zero. In ordinary rows, form is inferred from the first and last non-space-like children when it is not authored; an only child uses infix form. A finite dictionary covers selected arithmetic, relation, fence, and punctuation operators; other entries use a deterministic fallback. Percentages scale each side's dictionary spacing, em uses the token's font size, and standalone rendering converts CSS pixels through Dpi / 96. Side spacing is applied outside a stretched glyph. Negative lengths, other units, and CSS expressions are outside this grammar. The full operator dictionary and embellished-operator contexts remain outside this subset. Scalar accent and bar operands retain these attributes when serialized back to MathML. A composite accent operand containing authored operator presentation fails with DRAWING_MATH_OPERATOR_PRESENTATION because the scalar accent model cannot preserve its separate operators.

Word OMML writing rejects expressions retaining authored operator form or side spacing with NotSupportedException; retain MathML and use the shared renderer when those values must survive. Native OneNote writing rejects those properties with ONENOTE_WRITE_MATH_LOSS.

The same immutable expression tree feeds native OneNote math and Word OMML adapters. The shared model includes right and left scripts, centered upper/lower limits, built-up and slashed fractions, delimiter lists, stacks, matrices, equation arrays, n-ary operators, accents, bars, boxes, and phantoms. OneNote maps all of those structures natively. Word maps the lossless OMML subset; Stack and StretchStack fail with NotSupportedException because OMML has no equivalent, and callers can choose EquationArray explicitly when that projection is intended. Drawing owns the AST, portable markup, measurement, and visual layout; each document package owns only its native codec. MathML and LaTeX parsing default to a nesting limit of 128 and expose bounded overloads; excessive nesting fails with OfficeMathParseException.Code == "DRAWING_MATH_DEPTH".

Find a conversion package

Use OfficeConversionCapabilityCatalog when an application needs to discover the package and public API for a source-to-target conversion. Each route includes accepted extensions, its fidelity model, the result type that carries diagnostics, and whether the route is available in the browser converter.

using OfficeIMO;

foreach (OfficeConversionCapability route in
         OfficeConversionCapabilityCatalog.FindBySourceExtension(".docx")) {
    Console.WriteLine($"{route.Id}: {route.PackageId} -> {route.TargetExtension}");
}

OfficeIMO.Core describes these routes but does not execute them. Add the package named by PackageId, call the API shown by Api, and inspect the returned result or report before accepting the output.

Examples

Build a reusable vector scene

using OfficeIMO.Drawing;

var drawing = new OfficeDrawing(width: 420, height: 180)
    .AddShape(new OfficeShape {
        Kind = OfficeShapeKind.Rectangle,
        Width = 420,
        Height = 180,
        FillGradient = OfficeLinearGradient.Horizontal(
            OfficeColor.Parse("#F8FBFF"),
            OfficeColor.Parse("#EAF4FF")),
        StrokeColor = OfficeColor.Parse("#B7D7F5"),
        StrokeWidth = 1
    }, x: 0, y: 0)
    .AddText("OfficeIMO.Drawing", 20, 18, 380, 32,
        new OfficeFontInfo("Aptos", 18, OfficeFontStyle.Bold),
        OfficeColor.Parse("#1F2937"),
        OfficeTextAlignment.Left)
    .AddShape(OfficeShape.RoundedRectangle(140, 44, 10), 20, 86)
    .AddText("Shared vector intent", 34, 98, 240, 24);

OfficeDrawingQualityReport report = OfficeDrawingQualityAnalyzer.Analyze(drawing);
// Compare the same scene with a smaller delivery canvas without changing it.
OfficeDrawingQualityReport target = OfficeDrawingQualityAnalyzer.Analyze(drawing, 200, 200);
if (report.HasIssues) {
    foreach (var issue in report.Issues) {
        Console.WriteLine($"{issue.Kind}: {issue.Message}");
    }
}

OfficeClipPath.Path(width, height, commands, fillRule) retains path offsets and control points within a declared canvas. Use it for inset masks whose coordinates must stay aligned with an image or drawing. Scale scales that canvas and its coordinates together. The content-sized Path(commands, fillRule) overload derives its dimensions from the occupied bounds and moves those bounds to the local origin.

Render paragraphs with independent formatting

AddRichTextParagraphs keeps paragraph alignment, margins, indentation and line spacing in one text frame. SVG, raster and PDF measure the paragraphs at render time with the drawing's configured fonts.

using OfficeIMO.Drawing;

var drawing = new OfficeDrawing(300, 160).AddRichTextParagraphs(new[] {
    new OfficeRichTextParagraph(new[] {
        new OfficeRichTextRun("Invoice ", 16, OfficeColor.Black),
        new OfficeRichTextRun("approved", 16, OfficeColor.Parse("#167A36"), bold: true)
    }, OfficeTextAlignment.Center, lineHeight: 24),
    new OfficeRichTextParagraph(new[] {
        new OfficeRichTextRun("Ready for payment", 12, OfficeColor.Black)
    }, margins: new OfficeTextPadding(8, 6, 8, 0), lineHeightFactor: 1.4)
}, 10, 10, 280, 140, padding: new OfficeTextPadding(6, 6, 6, 6));

Absolute lineHeight applies to every visual line; lineHeightFactor scales each line's text extent. Choose one or leave both unset for the shared default. Hard line breaks continue the paragraph's indentation; a new paragraph restarts it. Adjacent vertical margins are added. The method snapshots caller collections and rejects inputs beyond 100,000 UTF-16 characters or 4,096 runs, counting paragraph separators. Layout omits paragraphs whose horizontal margins consume the frame, then continues with later paragraphs. Lines beyond the frame height are omitted; condensed line spacing can let glyph ink extend outside the line box. It does not infer native document auto-sizing.

The overload with OfficeTextAreaAlignment positions the whole paragraph block independently of paragraph alignment. FullWidth preserves the default frame width. Left, Center and Right use the widest measured line, including paragraph margins, and anchor that intrinsic area inside the padded frame. Shorter lines keep their paragraph alignment within it. Wrapping is measured once at the authored frame width; anchoring retains those line breaks. With wrapText: false, an oversized centered or right area can extend beyond either side of the frame.

var anchored = new OfficeDrawing(300, 100).AddRichTextParagraphs(new[] {
    new OfficeRichTextParagraph(new[] {
        new OfficeRichTextRun("Longer heading\nShort", 12, OfficeColor.Black)
    }, OfficeTextAlignment.Right)
}, 10, 10, 280, 80, OfficeTextAreaAlignment.Center, wrapText: false,
    padding: new OfficeTextPadding(12, 4, 9, 4));

Use OfficeTextParagraphLabel to style and position a list label independently of its body:

var label = OfficeTextParagraphLabel.InBox(
    new OfficeRichTextRun("1.", 12, OfficeColor.Black),
    position: 0, minimumWidth: 24, alignment: OfficeTextAlignment.Right,
    minimumDistance: 4);
var item = new OfficeRichTextParagraph(
    new[] { new OfficeRichTextRun("A body that can wrap", 12, OfficeColor.Black) },
    label, margins: new OfficeTextPadding(24, 0, 0, 0));

InBox expands a minimum-width label box when necessary. AtPosition aligns at an anchor and follows the label with a declared text position, one measured space, or no separator. Labels appear once on the first line, including items with empty body text, and contribute to automatic line height and shared character/run limits. Wrapped body text uses the paragraph's continuation indentation. A label that exceeds the content rectangle's right edge or right margin is omitted as a whole and marks the layout as clipped; logical text and body insets remain intact. Positions are measured from the text frame's content rectangle before render scaling; native tab-stop selection is the caller's responsibility.

Use WithTabStops to position tabbed fields with the same measurements in SVG, raster and PDF:

var prices = new OfficeRichTextParagraph(new[] {
    new OfficeRichTextRun("Item\t123.45", 12, OfficeColor.Black)
}).WithTabStops(new OfficeTextTabStops(new[] {
    new OfficeTextTabStop(180, OfficeTextTabAlignment.Character, ".").WithLeader(".")
}, defaultInterval: 36));

Stops support left, center, right and character alignment. Field measurement extends to the next tab or hard break across styled runs; an absent delimiter aligns the field's end. An aligned field that would overlap preceding content consumes its stop with zero advance. Default stops repeat on the interval grid beyond the last explicit stop. Positions are relative to the paragraph's inner left margin; origin changes that reference point. Leading and consecutive tabs advance normally, and hard breaks reset the line position.

Tabbed lines keep their fixed grid by default. Use WithParagraphAlignment() on the tab settings to move each tabbed line as a whole with the paragraph's center or right alignment; AlignWithParagraph reports this choice. Field alignment within the line is preserved, and tabbed lines are never justified. Pass false to restore the fixed grid. Plain lines retain paragraph alignment with either setting.

Soft wraps use continuation indentation; a tab beyond the frame marks layout as clipped and retains the following text without an unbounded gap. Paragraphs without tab settings retain legacy space expansion. Settings snapshot at most 256 unique stops and survive scaling, scene cloning and tinting; logical text retains the tab characters.

WithLeader returns an independent stop that repeats one non-control Unicode scalar in its measured gap. Pass null to remove it; whitespace leaves a blank gap. Leader paint uses the tab run's font and formatting, measures the complete repeated run, and retains the following field's anchor. Scaling and frame fitting retain the leader. A text-frame layout generates at most 100,000 UTF-16 characters of leader paint across all paragraphs; exhausting that budget marks clipping while retaining spacing and body text. Frame fitting does not shrink body text merely because leader paint reaches this limit. The logical model contains the original tabs; SVG, raster and PDF paint the generated glyphs.

Use WithLeaderStyle for separate textual formatting:

var styledStop = new OfficeTextTabStop(180).WithLeader(".").WithLeaderStyle(
    new OfficeTextTabLeaderStyle(fontSizeFactor: 1.5, color: OfficeColor.Blue, italic: true));

Unspecified properties inherit each actual tab run, including font family, bold/italic state, decorations and baseline. fontSizePoints overrides the relative multiplier and follows drawing scale and frame fitting. Color includes alpha by default; inheritOpacity: true keeps the active run's alpha, and an explicit opacity overrides either source. A transparent background override clears inherited background paint. The style is immutable, survives cloning and tinting, and has no effect on line leaders or a blank SPACE leader. Editing the glyph preserves its style; passing null to WithLeaderStyle removes only the formatting overrides. Unrepresentable relative font sizes suppress leader paint, report clipping and retain the body and tab advance.

Use WithLineLeader for vector paint that adds no characters to extracted text:

var ruledStop = new OfficeTextTabStop(180).WithLineLeader(
    new OfficeTextTabLineLeader(OfficeTextTabLineLeaderStyle.DotDash,
        doubleLine: true, widthPoints: 0.75, color: OfficeColor.Blue));

Line leaders support solid, dotted, dash, long dash, dot dash, dot dot dash and wave patterns, with optional parallel double lines. None retains a blank gap. A null color uses the tab's active text color. An absolute widthPoints follows drawing scale; otherwise widthFontFraction follows the active rendered font size, including frame fitting. Patterns and their painted bounds share one layout plan across SVG, raster and PDF. A declared LeaderText, including SPACE, takes precedence; each editing method preserves the other declaration, and null clears only its own kind. Vector paint is bounded to 8,192 vertices per tab and 100,000 per text frame. Truncation reports clipping and retains field positions and body font sizes. Pattern spacing and wave outlines are an approximation profile, not a promise of identical typography across native producers.

Affine effect groups contribute their transformed child bounds, not the dimensions of their temporary rendering buffers. Empty groups do not create overflow findings.

Render a chart snapshot to drawing primitives

using OfficeIMO.Drawing;

var snapshot = new OfficeChartSnapshot(
    name: "RevenueChart",
    title: "Revenue by quarter",
    chartKind: OfficeChartKind.ColumnClustered,
    data: new OfficeChartData(
        new[] { "Q1", "Q2", "Q3", "Q4" },
        new[] {
            new OfficeChartSeries("Revenue", new[] { 10d, 18d, 24d, 30d }),
            new OfficeChartSeries("Forecast", new[] { 12d, 19d, 25d, 33d })
        }),
    widthPoints: 420,
    heightPoints: 260);

OfficeChartRenderingResult rendered = OfficeChartDrawingRenderer.RenderWithQuality(snapshot);
OfficeDrawing chartDrawing = rendered.Drawing;

foreach (var issue in rendered.QualityReport.Issues) {
    Console.WriteLine(issue.Message);
}

Set a legend frame fill and outline through the shared chart style:

var framedStyle = new OfficeChartStyle(
    showBackground: true,
    legendBackgroundColor: OfficeColor.Parse("#FFFFFF"),
    legendBorderColor: OfficeColor.Parse("#375A7F"),
    legendBorderWidth: 1);
var framedSnapshot = new OfficeChartSnapshot("RevenueChart", "Revenue by quarter",
    OfficeChartKind.ColumnClustered, snapshot.Data, 420, 260, style: framedStyle);
OfficeDrawing framedChart = OfficeChartDrawingRenderer.Render(framedSnapshot);

The background and border are optional; the border width is a positive value in points. These settings style the chart legend frame in shared static renders.

Use layout.WithSecondaryValueAxis(new OfficeChartValueAxisLayout(minimum: 0, maximum: 1, majorUnit: 0.2, numberFormat: "0%").WithTitle("Completion rate")) for an independent secondary value-axis scale and title. Updating the scale without WithTitle preserves an existing native title; WithTitle(null) removes it explicitly. Use optional majorTickMark and minorTickMark settings to select independent tick appearance. The returned layout preserves the primary axis settings and leaves the original layout unchanged. Bounds must be finite and ordered; tick units must be positive. Explicit numeric bounds clip line, area, and scatter series paint to the plot; points outside the range do not gain false edge markers or labels. Data labels for visible points can extend beyond the plot.

Style individual chart points

Use OfficeChartSeries.WithSeriesStyle for independent series fill and outline paint, and WithPointStyles for overrides aligned with the series values. Both use OfficeChartPointStyle. For example, .WithSeriesStyle(new OfficeChartPointStyle(noFill: true, outlineColor: OfficeColor.Red, outlineWidth: 2, showOutline: true)) draws an unfilled area, column or bubble with a visible red outline. Point fills override inherited no-fill; outline-only point overrides keep the series fill policy. Line series use outline paint for their connecting line; their marker paint remains independent. The returned series preserves its data and other settings. A null entry inherits the point colour; an explicit no-fill style leaves an outlined slice visible without changing its value.

var status = new OfficeChartSeries("Status", new[] { 8d, 2d, 1d })
    .WithPointStyles(new OfficeChartPointStyle?[] {
        new(fillColor: OfficeColor.Parse("#168A56")),
        new(noFill: true, outlineColor: OfficeColor.Black, outlineWidth: 2,
            outlineJoin: OfficeStrokeLineJoin.Round),
        new(hatch: OfficeChartHatchPattern.WideForwardDiagonal,
            hatchColor: OfficeColor.Parse("#7300A3"), outlineColor: OfficeColor.Black)
    });

Hatches support horizontal, vertical, forward diagonal, backward diagonal, cross, diagonal cross, and wide forward diagonal strokes. Their default background is white; set fillColor to choose another background. Outline widths use points; joins can be round, bevel, or miter. showOutline: false hides the outline explicitly. The renderer clips hatches to slice, bar, and marker geometry and styles category legend swatches with the same appearance. Per-point area styling is not applied by static rendering; RenderWithQuality reports UnsupportedAppearance and retains the opaque series fill. An area series with no native outline renders without a connecting line.

Pie and doughnut geometry

Pass an OfficeChartRadialLayout as the final argument to the style/layout snapshot constructor to set the first slice boundary and doughnut hole. Rotation is clockwise from the top, from 0 through 360 degrees; hole size is an inner-to-outer diameter percentage from 10 through 90, with a default of 50.

var series = new OfficeChartSeries("Status", new[] { 7d, 3d })
    .WithPointExplosions(new[] { 25, 0 });
var data = new OfficeChartData(new[] { "Complete", "Pending" }, new[] { series });
var radial = new OfficeChartRadialLayout(firstSliceAngleDegrees: 90, doughnutHolePercent: 70);
var snapshot = new OfficeChartSnapshot("Status", null, OfficeChartKind.Doughnut,
    data, 400, 300, null, new OfficeChartLayout(showLegend: false), radial);
OfficeDrawing drawing = OfficeChartDrawingRenderer.Render(snapshot);

Multiple doughnut series form contiguous rings, starting with the first series at the inside. WithPointExplosions offsets selected pie or doughnut slices by a percentage of their radius while preserving their native editable point records in Word, PowerPoint, and Excel. Supply one value per point, from 0 through 400; an explicit zero resets an earlier offset. The renderer keeps exploded slices within the chart frame and moves their value-label anchors with them. For outside labels, set showDataLabels: true, select the label contents, and use dataLabelPosition: OfficeChartDataLabelPosition.OutsideEnd in OfficeChartLayout. The renderer reserves side gutters, separates labels on each side and connects them to visible slices. Call layout.WithDataLabelLeaderLines(false) to omit the lines. For multi-series doughnuts, leaders work when DataLabelSeriesIndexes selects only the outer ring. Disable leaders if labeled inner rings would send lines through outer rings. Imported Word, PowerPoint and Excel charts retain that native line setting in their static chart snapshots. A frame too narrow or short to fit every outside label reports an unsupported layout instead of overlapping or dropping labels. For imported pie and doughnut charts, the first six varyColors points use the document theme colors from a direct, untransformed modern chart color style, or theme accents for an unstyled/classic style 2 chart. Native point fills take precedence. Transformed color styles and longer sequences still need producer-specific proof.

Load first-party font programs for renderers

using System.Collections.Generic;
using System.IO;
using OfficeIMO.Drawing;

OfficeTrueTypeFont? font = OfficeTrueTypeFont.TryLoadDefault(out string? path);
if (font != null) {
    Console.WriteLine($"Loaded {path}");
}

var embedded = new OfficeFontFaceCollection {
    FontVariationResolver = request => request.FamilyName == "Report Variable"
        ? new Dictionary<string, float> { ["wght"] = 720 }
        : null
};
embedded.Add("Report Variable", File.ReadAllBytes("ReportVariable.ttf"));

OfficeFontFaceCollection accepts TrueType-glyf OpenType, WOFF 1, CFF/CFF2, and TrueType or CFF2 variable fonts. Single-face WOFF 2 decoding is available on .NET 8 and newer; extract and register individual faces from WOFF 2 font collections. The engine is part of OfficeIMO.Core; it does not require another font-program package or a license key.

For supported TrueType runs, the managed shaper uses the selected variation coordinates when resolving glyphs and advances. Distinct instances of the same source font keep distinct measurements. Extract a single face from a variable TrueType font collection before selecting variation axes.

What it provides

  • DocumentAccessMode, DocumentPersistenceMode, DocumentCreateOptions, and DocumentLoadOptions for one lifecycle vocabulary across document packages.
  • Dependency-free IOfficeSecurityProvider, CMS/X.509/XML-signature requests, options, findings, and results under the OfficeIMO.Security namespace. The same layer owns bounded OPC, VBA, and ODF/EPUB XML package-signature structure and atomic commit policy. The optional OfficeIMO.Security package supplies the concrete cryptographic provider.
  • Dependency-free provenance inspection and selective removal contracts under OfficeIMO.Provenance, including exact C2PA carriers and IPTC AI-source declarations. Optional cryptographic verification remains in OfficeIMO.Security.
  • OfficeColor immutable RGBA values with named colors and hex parsing.
  • OfficeColorSpaceConverter for dependency-free CMYK, CIE Lab/XYZ, calibrated gray, and calibrated RGB conversion to sRGB.
  • OfficeImageReader and OfficeImageInfo for dependency-free image inspection where supported.
  • OfficeImageFit for shared stretch, contain, and cover intent.
  • OfficeFontInfo, OfficeFontStyle, OfficeTextMeasurer, and OfficeTextMetrics for deterministic layout estimates.
  • OfficeTrueTypeFont and OfficeFontFaceCollection for first-party static and variable font measurement, glyph contours, and bounded renderer integration.
  • OfficeInkDocument, OfficeInkStroke, sampled pressure/style metadata, recognition alternatives, and OfficeInkRenderer for reusable ink capture and projection.
  • OfficeMathExpression, OfficeMath, MathML/LaTeX conversion, deterministic measurement, and OfficeMathRenderer for reusable structured equations.
  • OfficeShape, OfficeDrawing, gradients, shadows, transforms, clipping, and vector descriptors that format-specific packages can map into their own coordinate systems.
  • Linear and radial gradients support encoded sRGB and linear-light RGB interpolation. Use gradient.WithColorInterpolation(OfficeGradientColorInterpolation.LinearRgb) to create a detached linear-light gradient; ColorInterpolation reports its mode. Cloning, coordinate transforms, SVG import/export and raster output retain the mode. SVG color-interpolation inherits through CSS ancestors, including inline styles, independently of gradient template references.
  • OfficeConicGradient.CreateDrawing expands clockwise stops into clipped vector regions for the shared raster, SVG and PDF routes. Color sampling uses premultiplied alpha. Narrow transitions can reuse the unused flat-region sampling budget; their final endpoint paint continues into an adjacent matching plateau when the budget permits, preventing repeated edge coverage from tinting that plateau. Wrap and near-center antialiasing remain approximations, with differences between PDF consumers.
  • OfficeRadialGradient.TransformCoordinates preserves radial fields under affine rotation, shear and reflection. Its CoordinateTransform maps gradient coordinates into normalized shape coordinates; cloning and opacity changes retain that mapping. SVG import expands bounded radial repeat/reflect fields within the shared stop budget and retains other fields with OfficeRadialGradient.WithSpreadMode(OfficeGradientSpreadMode.Repeat) or Reflect. Cloning, transforms, raster sampling and SVG export preserve the spread mode. PDF export still requires finite expansion. Native boundary/exterior fields export as SVG 2 vector patterns with separate color and alpha composition. The shared SVG importer accepts shrinking-circle Pad, Repeat and Reflect fields, including point ends, and retains transparent regions outside ordinary SVG cones. Ordinary SVG point-focus boundary spreads retain their offset-weighted outside color and alpha; native XPS keeps its distinct endpoint rule. Pattern content retains its tile origin and stroke coverage offsets.
  • OfficeChartSnapshot and chart rendering primitives shared by PDF and Office exporters.
  • OfficeRasterImage, OfficeRasterCanvas, OfficeRasterRenderTarget, and OfficeDrawingRasterRenderer for shared dependency-free raster rendering.
  • OfficePngReader, OfficePngWriter, and OfficeJpegCodec for PNG/JPEG paths that should not be reimplemented by document packages.
  • OfficeTiffCodec, OfficeWebpCodec, and OfficeRasterImageEncoder for shared bounded TIFF, WebP decoding and lossless or lossy WebP encoding, and format-neutral raster output.
  • Shared SVG formatting, primitive writing, image projection, text-block rendering, hatch-pattern, data-bar, and sparkline helpers.
  • Drawing quality diagnostics for canvas bounds and text overlap checks.

Set OfficeDrawingRasterRenderOptions.ThrowOnImageDecodeFailure to true when every image must render. An unsupported image, failed optional codec, or decoded raster above MaximumRasterPixels then stops rendering with NotSupportedException. Successful image decoding happens in the drawing pass, including nested groups and patterns.

High-quality image minification shares the operation's MaximumRasterPixels budget with decoded images. The renderer charges its additional output buffer and floating-point sampling scratch before allocation. When that budget prevents an SVG safety comparison, structural findings remain available and the report identifies the unavailable visual inspection.

Boundaries

  • This package owns shared lifecycle contracts, persistence mechanics, drawing intent, ink/math models and renderers, raster buffers, SVG and raster encoding primitives, image projection, text layout helpers, chart drawing, and document-agnostic visual diagnostics.
  • Security contracts live here so format packages can expose strongly typed opt-in APIs without depending on the optional cryptographic package. Core includes only the portable AES-CBC compatibility provider; CMS, XML DSig, certificate-chain, and private-key operations remain in OfficeIMO.Security.
  • Word, Excel, PowerPoint, Visio, and PDF packages own source-document semantics: package parsing, layout policy, coordinate systems, style/theme resolution, and user-facing export APIs.
  • Document packages should not add private ink or math ASTs, pixel engines, image encoders/decoders, SVG primitive writers, text wrapping engines, or duplicate image-transform loops when the behavior can reasonably live here.
  • PDF keeps PDF-stream and page-writer behavior in OfficeIMO.Pdf; when it needs generic image-like drawing, vector descriptors, colors, chart snapshots, PNG helpers, or raster visual QA, it should use OfficeIMO.Core and the OfficeIMO.Drawing namespace.
  • Unsupported or approximate source features belong in stable diagnostics from the adapter, not as silent omissions in a renderer.

Targets and license

Dependency footprint

  • External: None.
  • OfficeIMO: This is the shared foundation; it does not depend on another OfficeIMO runtime package.

See the complete OfficeIMO package map for related formats and conversion paths.

Inspected caller raster decoding

OfficeRasterDecodeOptions.ImageCodec supplies a trusted decoder for inspected payloads outside the managed subset. The shared boundary validates resource limits, frame selection and returned dimensions, preserves input bytes, and observes cancellation before and after the callback. Static WebP decoding failures do not fall through to a caller codec. Animated WebP can select frame zero and reports discarded animation. Drawing exports retain caller provenance and distinguish visible failure placeholders from decoded source pixels.

Bounded AVIF still images

AVIF decoding accepts bounded, whole, untransformed 8/10-bit YUV420 color or monochrome grayscale items with reduced or full still-picture headers and optional same-depth full-range monochrome alpha. Full headers use one unlayered operating point, no timing/decoder model, and one shown key frame in a combined frame OBU. Primary grayscale items may use full or limited range; an auxiliary alpha plane must use full range. It produces eight-bit straight-alpha RGBA using the declared CICP range and supported non-constant-luminance matrix. It does not apply ICC, transfer-function or gamut transforms. Image grids, image sequences, twelve-bit coding, crop/rotation properties and applied film grain are outside this decoder contract. The original encoded buffer, reconstruction planes and final pixels share the retained-memory limit; cancellation and work limits apply through reconstruction and composition. Malformed selected items and limit failures cannot invoke a caller codec. A validated item with an unsupported color matrix or vertical/colocated chroma placement may use the explicitly supplied ImageCodec; managed YUV420 composition currently qualifies centered/unspecified chroma placement.

Generated capability summary

This table is generated from the package-neutral OfficeIMO operation catalog. The detailed source contracts remain authoritative for feature-level behavior and limitations.

Operation Supported Partial Preserved Rejected Unsupported Not applicable
Inspect 2 0 0 0 0 0
Validate 2 0 0 0 0 0
Remove 2 0 0 0 0 0

The complete rows for OfficeIMO.Core are published in the generated operation contract.

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.
  • .NETFramework 4.7.2

    • No dependencies.
  • .NETStandard 2.0

    • No dependencies.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.

NuGet packages (81)

Showing the top 5 NuGet packages that depend on OfficeIMO.Core:

Package Downloads
OfficeIMO.Word

Open-source, cross-platform .NET library for creating, reading, editing, and converting Microsoft Word DOC, DOCX, DOCM, DOT, DOTX, and DOTM documents without Microsoft Office.

OfficeIMO.Markdown

Markdown builder, typed reader/AST, and HTML renderer for .NET with GitHub-like output and an optional portable reader profile.

OfficeIMO.Pdf

First-party PDF builder, reader, editor, renderer, and signature workflow for .NET.

OfficeIMO.Rtf

Managed Rich Text Format (RTF) reader, writer, lossless syntax tree, bounded parser, and editable document model for .NET.

OfficeIMO.Html

HTML parsing, resource policy, CSS layout, diagnostics, and direct rendering for OfficeIMO converters.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.5.2 1,523 10/24/2026
3.5.1 8,039 10/16/2026
3.4.5 10,751 10/10/2026
3.4.4 19,051 9/27/2026
3.4.3 19,947 9/12/2026
3.4.2 4,901 9/9/2026
3.4.1 3,929 9/7/2026
3.4.0 3,437 9/6/2026
3.3.0 4,405 9/2/2026
3.2.7 5,594 8/29/2026
3.2.6 11,655 8/22/2026
3.2.5 3,131 8/21/2026
3.2.4 9,263 8/19/2026
3.2.3 5,441 8/17/2026
3.2.2 10,129 8/13/2026
3.2.1 3,318 8/11/2026
3.2.0 27,769 8/7/2026
3.1.1 2,704 8/7/2026
3.1.0 4,250 8/6/2026