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();
| Method | Behavior |
|---|---|
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 level | JPEG quality | Use case |
|---|---|---|
Best | 100 | Maximum quality, largest file size |
High | 90 | Default — visually lossless for most content |
Medium | 75 | Good balance of quality and size |
Low | 50 | Noticeable compression, smaller files |
VeryLow | 25 | Aggressive 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);
| Option | Behavior |
|---|---|
AspectRatioOption.FitWidth | Uses full available width, calculates height from ratio (default) |
AspectRatioOption.FitHeight | Uses full available height, calculates width from ratio |
AspectRatioOption.FitArea | Fits 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
| Format | Read (decode) | Write (encode) | Notes |
|---|---|---|---|
| PNG | Yes | Yes | Lossless, supports transparency |
| JPEG | Yes | Yes | Lossy, no transparency, smallest for photos |
| WebP | Yes | Yes | Both lossy and lossless modes |
| BMP | Yes | No | Uncompressed raster |
| GIF | Yes | No | First frame only (no animation) |
| ICO | Yes | No | First icon in the container |
| SVG | Yes | Yes | Via Skia's SVG module; .Svg() API |