How Inline Images Work

Images are stored in the text stream as U+FFFC (Object Replacement Character) placeholders. Each placeholder carries an InlineImage object that the RTK layout engine calls to measure and paint the image. From the user's perspective, images behave exactly like characters: they participate in selection, copy/paste, undo/redo, and wrap with the surrounding text.

📌

Click to Select

Clicking an image auto-selects the 1-character range and shows 8 resize handles.

↔️

Resize Handles

Corners maintain aspect ratio; edge handles allow free resize in one axis. Drag to resize live.

🔄

Undo / Redo

Every resize is committed as an undoable operation on pointer release.

📋

Clipboard Paste

Pasting bitmap images from the clipboard inserts them as inline objects automatically.

InlineImage

class

Implements IInlineObject (from Topten.RichTextKit) to participate in text layout.

MemberTypeDescription
Widthfloat { get; set; }Display width in pixels. Mutable to support live resize without undo overhead.
Heightfloat { get; set; }Display height in pixels. Mutable for the same reason.
ImageSKImageThe underlying Skia image (read-only).
InlineImage(SKImage, float w, float h)ctorCreate with an existing SKImage and display dimensions.
Paint(SKCanvas, SKPoint)voidCalled by RTK to draw the image during layout. Do not call directly.
GetPngBytes()byte[]Returns the image encoded as PNG bytes — used by exporters.

Inserting Images

Use DocumentController.InsertInlineImage(). It automatically clamps the display dimensions so the image does not exceed the available content width.

// Load from a file path
using var skData = SKData.Create("photo.png");
var skImage = SKImage.FromEncodedData(skData);

// Insert at the current caret position
controller.InsertInlineImage(skImage, displayWidth: 400f, displayHeight: 300f);

// Or load from a stream
await using var stream = File.OpenRead("photo.jpg");
var bitmap = new Bitmap(stream);
// convert Avalonia Bitmap → SKImage via MemoryStream if needed

ResizeHandleType

enum

Identifies which of the eight resize handles the user is interacting with. Used internally by DocumentController.

ValuePositionBehavior
NoneNo handle active
TopLeftTop-left cornerMaintains aspect ratio
TopCenterTop edge midpointFree resize (vertical only)
TopRightTop-right cornerMaintains aspect ratio
MiddleRightRight edge midpointFree resize (horizontal only)
BottomRightBottom-right cornerMaintains aspect ratio
BottomCenterBottom edge midpointFree resize (vertical only)
BottomLeftBottom-left cornerMaintains aspect ratio
MiddleLeftLeft edge midpointFree resize (horizontal only)

EditorCursor

enum

A platform-agnostic cursor hint sent to the UI via DocumentController.RequestCursorUpdate. The UI layer is responsible for mapping these to native cursor types.

ValueWhen UsedAvalonia Mapping
DefaultNormal text areaStandardCursorType.Ibeam
ResizeNSTop or bottom edge handleStandardCursorType.SizeNorthSouth
ResizeEWLeft or right edge handleStandardCursorType.SizeWestEast
ResizeDiagNWSETopLeft or BottomRight cornerStandardCursorType.TopLeftCorner
ResizeDiagNESWTopRight or BottomLeft cornerStandardCursorType.TopRightCorner
MoveImage body (drag-to-move)StandardCursorType.SizeAll
// Wire cursor updates in your Avalonia control
controller.RequestCursorUpdate = (cursor) =>
{
    Cursor = cursor switch
    {
        EditorCursor.ResizeNS       => new Cursor(StandardCursorType.SizeNorthSouth),
        EditorCursor.ResizeEW       => new Cursor(StandardCursorType.SizeWestEast),
        EditorCursor.ResizeDiagNWSE => new Cursor(StandardCursorType.TopLeftCorner),
        EditorCursor.ResizeDiagNESW => new Cursor(StandardCursorType.TopRightCorner),
        EditorCursor.Move           => new Cursor(StandardCursorType.SizeAll),
        _                            => new Cursor(StandardCursorType.Ibeam),
    };
};

Exporting Images

When iterating with DocumentReader, image runs are identified by ContentRun.IsImage == true. The PNG bytes are available via ContentRun.ImageData.

foreach (var run in block.Runs)
{
    if (run.IsImage)
    {
        // Embed as base-64 in HTML
        var b64 = Convert.ToBase64String(run.ImageData);
        html += $"<img src='data:image/png;base64,{b64}' "
             +  $"width='{run.ImageWidth}' height='{run.ImageHeight}'>";

        // Or save to a file
        File.WriteAllBytes("image.png", run.ImageData);
    }
}