Images & SVG

Embed raster images, SVG graphics, dynamically generated images, and inline SVG paths. Control scaling, compression, and aspect ratio for pixel-perfect output.

Raster Images

Add images from byte arrays, file paths, or streams. FolioPDF supports PNG, JPEG, WebP, and any format Skia can decode.

From a byte array

byte[] logoBytes = File.ReadAllBytes($"logo.png");
col.Item().Image(logoBytes);

From a file path

col.Item().Image($"photos/team.jpg");

From a stream

using var stream = File.OpenRead($"chart.png");
col.Item().Image(stream);

Image Scaling Modes

By default, an image takes its natural pixel dimensions. Use scaling methods on the returned ImageDescriptor to control how the image fits within its container.

// Scale to fill available width, height proportional
col.Item().Image(imageBytes).FitWidth();

// Scale to fill available height, width proportional
col.Item().Height(200).Image(imageBytes).FitHeight();

// Scale to fit entirely within available area (may leave gaps)
col.Item().Width(300).Height(200).Image(imageBytes).FitArea();

// Stretch to fill (may distort)
col.Item().Width(300).Height(200).Image(imageBytes).FitUnproportionally();
MethodBehavior
FitWidth()Scale uniformly to fill available width; height adjusts proportionally
FitHeight()Scale uniformly to fill available height; width adjusts proportionally
FitArea()Scale to fit entirely within the available rectangle (letterboxing)
FitUnproportionally()Stretch to fill the exact rectangle (may distort aspect ratio)

Constraining Image Size

Combine image scaling with size constraints for precise control.

// Logo: 120pt wide, height proportional
col.Item().Width(120).Image(logoBytes).FitWidth();

// Thumbnail: max 80x80pt, maintain aspect ratio
col.Item().MaxWidth(80).MaxHeight(80).Image(photoBytes).FitArea();

// Banner: full width, exactly 150pt tall
col.Item().Height(150).Image(bannerBytes).FitWidth();

Image Compression Settings

Control how images are compressed in the output PDF. By default, opaque images are JPEG-compressed and transparent images use PNG.

// Use original image data without any recompression
col.Item().Image(imageBytes).UseOriginalImage();

// Set target DPI for downsampling
col.Item().Image(imageBytes).WithRasterDpi(150);

// Set compression quality
col.Item().Image(imageBytes).WithCompressionQuality(ImageCompressionQuality.Medium);
Quality levelJPEG qualityUse case
Best100Maximum quality, largest file size
High90Default — visually lossless for most content
Medium75Good balance of quality and size
Low50Noticeable compression, smaller files
VeryLow25Aggressive compression for minimum file size

Document-Level Image Settings

Set default image compression for the entire document via DocumentSettings.

Document.Create(doc => { /* ... */ })
.WithSettings(new DocumentSettings
{
    ImageCompressionQuality = ImageCompressionQuality.Medium,
    ImageRasterDpi = 150   // lower DPI = smaller files
})
.GeneratePdf($"optimized.pdf");

AspectRatio Element

The AspectRatio element constrains its child to maintain a specific width-to-height ratio, regardless of available space.

// 16:9 aspect ratio, fills available width
col.Item().AspectRatio(16f / 9f).Image(wideImageBytes).FitArea();

// Square (1:1)
col.Item().Width(200).AspectRatio(1).Background(Color.LightGrey)
    .AlignCenter().AlignMiddle().Text($"Square");

// Options: FitWidth (default), FitHeight, FitArea
col.Item().AspectRatio(4f / 3f, AspectRatioOption.FitHeight)
    .Image(imageBytes);
OptionBehavior
AspectRatioOption.FitWidthUses full available width, calculates height from ratio (default)
AspectRatioOption.FitHeightUses full available height, calculates width from ratio
AspectRatioOption.FitAreaFits within available area, preserving ratio

DynamicImage

Generate images at render time whose pixel dimensions are determined by the layout engine. Ideal for charts, maps, QR codes, and other content that depends on available space.

// Simple overload: receives pixel dimensions, returns PNG/JPEG/WebP bytes
col.Item().Width(400).Height(300).Image(size =>
{
    // size.Width / size.Height are in pixels at the target DPI
    return GenerateChart(size.Width, size.Height);
});

// Full overload: receives available space, image size, and DPI
col.Item().Image((GenerateDynamicImageDelegate)(payload =>
{
    // payload.AvailableSize  - layout space in PDF points
    // payload.ImageSize      - target pixel dimensions
    // payload.Dpi            - target DPI
    return RenderMap(payload.ImageSize.Width, payload.ImageSize.Height);
}));

DynamicImage Descriptor

Configure the dynamic image's DPI and compression after creation.

col.Item().Width(500).Height(350)
    .Image(size => GenerateHeatmap(size.Width, size.Height))
    .WithRasterDpi(300)
    .WithCompressionQuality(ImageCompressionQuality.High)
    .UseOriginalImage(false);

SVG Rendering

Render SVG documents from string content. The SVG is rasterized by Skia's SVG module and embedded as a vector graphic in the PDF.

string svgContent = File.ReadAllText($"diagram.svg");
col.Item().Svg(svgContent);

SVG Scaling

The returned SvgImageDescriptor supports the same scaling modes as raster images.

// Fill available width (default)
col.Item().Svg(svgContent).FitWidth();

// Fill available height
col.Item().Height(200).Svg(svgContent).FitHeight();

// Fit within available area
col.Item().Width(300).Height(200).Svg(svgContent).FitArea();

SVG with Size Constraints

// Logo from SVG, constrained to 150pt wide
col.Item().Width(150).Svg(logoSvg).FitWidth();

// Icon at exact dimensions
col.Item().Width(24).Height(24).Svg(iconSvg).FitArea();

DynamicSvgImage

Generate SVG content at render time, where the SVG markup depends on the available layout space.

col.Item().Svg((GenerateDynamicSvgDelegate)(payload =>
{
    // payload.AvailableSize gives the available space in PDF points
    float width = payload.AvailableSize.Width;
    float height = payload.AvailableSize.Height;

    return $"<svg viewBox=\"0 0 {width} {height}\" xmlns=\"http://www.w3.org/2000/svg\">" +
        $"<rect width=\"{width}\" height=\"{height}\" fill=\"#E8F0FE\" rx=\"8\"/>" +
        $"<text x=\"{width / 2}\" y=\"{height / 2}\" text-anchor=\"middle\"" +
        $" dominant-baseline=\"central\" font-size=\"14\" fill=\"#333\">" +
        $"Dynamic SVG ({width:F0} x {height:F0})" +
        "</text></svg>";
}));

SvgPath (Inline Path Data)

Render a single SVG path string as a filled shape. Useful for icons and simple vector graphics without a full SVG document wrapper.

// Checkmark icon
col.Item().Width(24).Height(24)
    .SvgPath($"M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z", Color.FromHex("#4CAF50"));

// Arrow icon
col.Item().Width(16).Height(16)
    .SvgPath($"M8 0l8 8-8 8V0z", Color.FromHex("#1A73E8"));

Practical Examples

Image Gallery

var photos = new[] { $"photo1.jpg", "photo2.jpg", "photo3.jpg",
                     $"photo4.jpg", "photo5.jpg", "photo6.jpg" };

col.Item().Inlined(inl =>
{
    inl.Spacing(10);

    foreach (var photo in photos)
    {
        inl.Item()
            .Width(150).Height(100)
            .Border(1, Color.FromHex($"#E0E0E0"))
            .Padding(2)
            .Image(photo).FitArea();
    }
});

Logo with Text

col.Item().Row(row =>
{
    row.AutoItem().Width(60).Height(60)
        .Image(logoBytes).FitArea();

    row.RelativeItem().PaddingLeft(12).AlignMiddle().Column(inner =>
    {
        inner.Item().Text($"TrueSpar Inc.").FontSize(20).Bold();
        inner.Item().Text($"Enterprise Software Solutions")
            .FontSize(11).FontColor(Color.Grey);
    });
});

Background Image with Overlay

col.Item().Height(300).Layers(layers =>
{
    // Background image
    layers.Layer().Image(backgroundBytes).FitWidth();

    // Semi-transparent overlay
    layers.Layer().Extend()
        .Background(Color.FromRGBA(0, 0, 0, 128));

    // Text on top
    layers.PrimaryLayer()
        .AlignCenter().AlignMiddle()
        .Text($"Welcome").FontSize(48).Bold().FontColor(Color.White);
});

Inline Icons in Text

col.Item().Text(t =>
{
    t.Span($"Status: ");
    t.Element(el =>
    {
        el.Width(14).Height(14)
            .SvgPath($"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 " +
                     $"10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 " +
                     $"1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z",
                Color.FromHex($"#4CAF50"));
    }, TextInjectedElementAlignment.AboveBaseline);
    t.Span($" Verified").FontColor(Color.FromHex("#4CAF50")).Bold();
});

Product Catalog Row

col.Item().Border(1, Color.FromHex($"#E0E0E0")).CornerRadius(8).Row(row =>
{
    // Product image
    row.ConstantItem(120).Padding(10)
        .Image(productImageBytes).FitArea();

    // Product details
    row.RelativeItem().Padding(12).Column(details =>
    {
        details.Spacing(4);
        details.Item().Text($"Premium Widget").FontSize(16).Bold();
        details.Item().Text($"High-quality stainless steel construction with " +
            $"ergonomic grip.").FontSize(10).FontColor(Color.FromHex("#666"));
        details.Item().Text($"$49.99").FontSize(14).Bold()
            .FontColor(Color.FromHex($"#1A73E8"));
    });
});

Performance tip: For documents with many images, set ImageRasterDpi to 150 (instead of the default 288) and use ImageCompressionQuality.Medium in DocumentSettings. This can reduce file size by 60–80% with minimal visual difference at normal viewing distances.

Supported Image Formats

FormatRead (decode)Write (encode)Notes
PNGYesYesLossless, supports transparency
JPEGYesYesLossy, no transparency, smallest for photos
WebPYesYesBoth lossy and lossless modes
BMPYesNoUncompressed raster
GIFYesNoFirst frame only (no animation)
ICOYesNoFirst icon in the container
SVGYesYesVia Skia's SVG module; .Svg() API