Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,9 @@ navigation:
libraries/radpdfprocessing/formats-and-conversion/pdf/pdfstreamwriter:
title: PdfStreamWriter
position: 2
libraries/radpdfprocessing/formats-and-conversion/pdf/expandablememorystream:
title: ExpandableMemoryStream
position: 3
libraries/radpdfprocessing/formats-and-conversion/plain-text:
title: Plain Text
position: 0
Expand Down
148 changes: 148 additions & 0 deletions knowledge-base/fit-multiline-text-stamp-annotation-radpdfprocessing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
---
title: Fit Multiline Text Inside a Custom Stamp Annotation in PDFs with RadPdfProcessing
description: Learn how to measure and fit multiline text inside the fixed rectangle of a custom StampAnnotation by calculating the optimal font size.
type: how-to
page_title: Fit Multiline Text Inside a Custom Stamp Annotation in PDFs with RadPdfProcessing
meta_title: Fit Multiline Text Inside a Custom Stamp Annotation in PDFs with RadPdfProcessing
slug: fit-multiline-text-stamp-annotation-radpdfprocessing
tags: pdf, stamp, annotation, font, size, multiline, measure, text, appearance, autosize, fit, aouto, fixed, editor, auto
res_type: kb
---

## Environment

| Version | Product | Author |
| ---- | ---- | ---- |
| 2025.3.806 | RadPdfProcessing | [Yoan Karamanov](https://www.telerik.com/blogs/author/yoan-karamanov) |

## Description

This article shows how to render multiple lines of text so they fit inside the fixed rectangle of a custom [StampAnnotation]({%slug radpdfprocessing-model-annotations-stamp%}) by dynamically determining the maximum usable font size. Unlike predefined stamp names, a custom stamp appearance requires supplying a visual form (a **FormSource**) that draws both background styling and text content.

>important When using a custom name (not one of the predefined **StampAnnotationPredefinedNames**), it is recommended to prefix the name with **#** (e.g., **#CustomStamp**) so external PDF viewers (like Adobe Acrobat) don't overwrite the custom appearance if the stamp is moved.

## Solution

To fit multiline text inside a stamp rectangle:

1. Create a **StampAnnotation** with a fixed **Rect** on a page.
2. Build a custom **FormSource** that will serve as the annotation's appearance (**Content.NormalContentSource**).
3. Measure each line by incrementally increasing the font size in a **Block** until the rendered dimensions exceed the available width/height and keep the maximum size that fits.
4. Use the minimum of all line-specific maximum sizes so every line fits within the rectangle.
5. Draw a decorative background (border, fill, dash pattern) and then render each line vertically stacked.
6. Export the document to PDF.

![Stamp With Auto-Fitted Multiline Text](images/stamp-auto-fitted-multiline-text.png)

Below is a complete example demonstrating these steps.

#### [C#] Measure and Fit Multiline Text in a StampAnnotation

```csharp
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Telerik.Documents.Primitives;
using Telerik.Windows.Documents.Fixed.FormatProviders.Pdf;
using Telerik.Windows.Documents.Fixed.Model;
using Telerik.Windows.Documents.Fixed.Model.Annotations;
using Telerik.Windows.Documents.Fixed.Model.ColorSpaces;
using Telerik.Windows.Documents.Fixed.Model.Editing;
using Telerik.Windows.Documents.Fixed.Model.Resources;

List<string> lines = new List<string>() { "1st line", "second line", "3rd line",};
RadFixedDocument document = CreateTextAnnotation(lines);

string exportFileName = "StampAnnotation.pdf";
File.Delete(exportFileName);
using (Stream output = File.OpenWrite(exportFileName))
{
PdfFormatProvider provider = new PdfFormatProvider();
provider.Export(document, output, TimeSpan.FromSeconds(20));
}

Process.Start(new ProcessStartInfo
{
FileName = exportFileName,
UseShellExecute = true
});

RadFixedDocument CreateTextAnnotation(List<string> lines)
{
RadFixedDocument radFixedDocument = new RadFixedDocument();
RadFixedPage page = radFixedDocument.Pages.AddPage();

StampAnnotation stampAnnotation = page.Annotations.AddStamp(new Rect(100, 100, 100, 100));
stampAnnotation.Name = "#Test";

FormSource annotationForm = CreateContentFormWithText(stampAnnotation.Rect, lines);
stampAnnotation.Content.NormalContentSource = annotationForm;

return radFixedDocument;
}

static FormSource CreateContentFormWithText(Rect stampRectangle, List<string> lines)
{
FormSource textForm = new FormSource();
textForm.Size = new Size(stampRectangle.Width, stampRectangle.Height);

FixedContentEditor textFormEditor = new FixedContentEditor(textForm);

using (textFormEditor.SaveProperties())
{
textFormEditor.GraphicProperties.IsFilled = true;
textFormEditor.GraphicProperties.IsStroked = true;
textFormEditor.GraphicProperties.StrokeThickness = 2;
textFormEditor.GraphicProperties.StrokeColor = new RgbColor(92, 229, 0);
textFormEditor.GraphicProperties.FillColor = new RgbColor(213, 222, 226);
textFormEditor.GraphicProperties.StrokeDashArray = new double[] { 17, 4 };
textFormEditor.DrawRectangle(new Rect(textFormEditor.Position.Matrix.OffsetX, textFormEditor.Position.Matrix.OffsetY, stampRectangle.Width, stampRectangle.Height));
}

double calculatedFontSize = MeasureFontSize(textForm, textFormEditor, lines);
textFormEditor.TextProperties.FontSize = calculatedFontSize;

double offset = 0;
foreach (string line in lines)
{
textFormEditor.Position.Translate(0, offset);
textFormEditor.DrawText(line);
offset += calculatedFontSize;
}

return textForm;
}

static double MeasureFontSize(FormSource textForm, FixedContentEditor formEditor, List<string> lines)
{
List<double> fontSizes = new List<double>();

foreach (string line in lines)
{
double fontSize = 0;
Size textSize = Size.Empty;
Block block = new Block();
block.TextProperties.Font = formEditor.TextProperties.Font;
while (textSize.Width < textForm.Size.Width && textSize.Height < textForm.Size.Height)
{
block.TextProperties.FontSize = ++fontSize;
block.InsertText(line);
textSize = block.Measure();
block.Clear();
}

fontSizes.Add(fontSize);
}

return fontSizes.Min();
}
```

## See Also

- [Stamp Annotation]({%slug radpdfprocessing-model-annotations-stamp%})
- [FixedContentEditor]({%slug radpdfprocessing-editing-fixedcontenteditor%})
- [FormSource Overview]({%slug radpdfprocessing-model-formsource-overview%})
- [PdfProcessing Overview]({%slug radpdfprocessing-overview%})
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
77 changes: 77 additions & 0 deletions knowledge-base/runtime-licensing-diagnostics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
title: Runtime Licensing Diagnostics
description: Step-by-step walkthrough of using Telerik.Licensing runtime diagnostics to investigate unexpected watermarks across web, plugin, and desktop scenarios.
type: how-to
page_title: Runtime Licensing Diagnostics
meta_title: Runtime Licensing Diagnostics
slug: runtime-licensing-diagnostics
tags: licensing, diagnostics, runtime, watermark, plugins, webforms, aspnet, blazor, wpf, winforms, license, key, register
ticketid: 1697496
res_type: kb
---

## Environment

| Version | Product(s) | Author |
| ---- | ---- | ---- |
| 2025.3.806 | Document Processing & Telerik UI |[Yoan Karamanov](https://www.telerik.com/blogs/author/yoan-karamanov)|

## Description

In certain environments an application may still show a trial watermark (or trial sheet) **at runtime** even though everything looked correct at **build time**:

* Valid **telerik-license.txt** file is present.
* Build logs indicate Telerik products detected (class library and executable).
* No licensing build errors are reported.

Yet the rendered UI (document, control, page) contains a watermark.

These issues surface mainly in two categories:

1. **Web products (ASP.NET AJAX, MVC, Core, Blazor)** – Incorrect script load order or duplicate script imports where one import overrides a previously applied license.
2. **.NET applications with extensibility / plugins (WPF, WinForms, class libraries, host wrappers)** - The entry assembly at runtime differs (e.g. a host like **SentinelDotNetFx.dll**), changing the licensing evaluation path.

Runtime diagnostics in **Telerik.Licensing** help correlate what the licensing engine sees (entry assembly, loaded assemblies, product metadata, license evidences) with the final **IsLicenseValid** result.

## Solution

### 1. Enable Diagnostics Early
Call **Telerik.Licensing.TelerikLicensing.EnableDiagnostics();** **as early as possible**, before any Telerik UI controls or Document Processing code is first reached/loaded.

### 2. Plugin / Non-Standard Entry Scenarios
If the code runs as a plugin or a dynamically loaded context (no traditional entry assembly, or an unexpected wrapper assembly), call **TelerikLicensing.Register()** **after** enabling diagnostics to provision runtime script keys.

### 3. Execute
Trigger the Telerik code so the licensing pipeline executes.

### 4. Collect the Log
Read the accumulated diagnostics text from **Telerik.Licensing.TelerikLicensing.Diagnostics** and persist it (file, console). The log grows for the life of the process (restart to clear).

### 5. Analyze Key Sections
Look for:
- Entry assembly (**Assembly.GetEntryAssembly()**)
- Product metadata extraction (**ProductMetadataForAssembly ...**)
- License evidences (subscription / perpetual tokens)
- Final resolution line (**Resolved: IsLicenseValid: ...**)

### 6. Disable When Done
Diagnostics add overhead and should not remain enabled indefinitely in production.

### Performance & Operational Notes
- Diagnostics add measurable overhead. Use only during investigation.
- Log size grows monotonically until process restart.
- Capture the log after reproducing the watermark; then disable diagnostics.

## See Also

* [License Activation Errors and Warnings]({%slug activation-errors-and-warnings%})
* [Adding the License Key to CI Services]({%slug adding-license-key-ci-cd-services%})
* [License Key FAQ]({%slug frequently-asked-questions%})
* [License Agreement]({%slug license-agreement%})
* [Redistributing Telerik Document Processing]({%slug installation-deploying-telerik-document-processing%})
* [Unable to find package Telerik.Licensing]({%slug dpl-package-update-failure-license%})
* [Handling License Key File Name and Environment Variable Name Changes in the 2025 Q1 Release]({%slug handling-license-file-name-changes%})
* [Telerik.Licensing NuGet package is not available on the Telerik NuGet feed]({%slug dpl-telerik-licensing-nuget-feed%})
* [Diagnostic Options for Telerik Licensing]({%slug telerik-trial-version-message-diagnostic-options%})
* [Resolving License Validation Issues in Telerik Document Processing Libraries]({%slug license-not-recognized-telerik-document-processing-libraries%})
* [Telerik License Approaches](https://github.com/LanceMcCarthy/DevOpsExamples?tab=readme-ov-file#telerik-license-approaches)
Original file line number Diff line number Diff line change
Expand Up @@ -41,46 +41,7 @@ RadFixedDocument stores the integrated files in an **EmbeddedFilesCollection** a

### Creating an Embedded Electronic (ZUGFeRD) Invoice

RadPdfProcessing provides support for embedding of [ZUGFeRD](https://de.wikipedia.org/wiki/ZUGFeRD) (acronym for Zentraler User Guide des Forums elektronische Rechnung Deutschland) invoices.

#### **[C#] Add ZUGFeRD invoice**

{{region cs-radpdfprocessing-embedded-file-add-zugferd-invoice}}

RadFixedDocument document = new RadFixedDocument();
using (RadFixedDocumentEditor editor = new RadFixedDocumentEditor(document))
{
editor.CharacterProperties.TrySetFont(new System.Windows.Media.FontFamily("Calibri"));
editor.InsertRun("PDF/A-3B Compliant Invoice");
};
byte[] bytes = File.ReadAllBytes(@"zugferd-invoice.xml");
document.EmbeddedFiles.AddZugferdInvoice(bytes);

PdfFormatProvider provider = new PdfFormatProvider();
PdfExportSettings settings = new PdfExportSettings();
settings.ComplianceLevel = PdfComplianceLevel.PdfA3B;
provider.ExportSettings = settings;
using (Stream output = File.OpenWrite("exportedInvoice.pdf"))
{
provider.Export(document, output);
}

{{endregion}}

>note Only a single XML invoice attachment is allowed according to ZUGFeRD standard.

>important To comply with the PDF/A-3B standard all the fonts in the documents should be embedded, so please avoid using [Standard Fonts]({%slug radpdfprocessing-concepts-fonts%}) because they are not being embedded in the document. In **.NET Standard/.NET (Target OS: None)** environments, fonts beyond the [14 standard ones]({%slug radpdfprocessing-concepts-fonts%}#standard-fonts) require a [FontsProvider implementation]({%slug pdfprocessing-implement-fontsprovider%}) to be resolved correctly.

#### **[C#] Remove ZUGFeRD invoice**

{{region cs-radpdfprocessing-embedded-file-remove-zugferd-invoice}}

if (document.EmbeddedFiles.ContainsZugferdInvoice)
{
document.EmbeddedFiles.RemoveZugferdInvoice();
}

{{endregion}}
RadPdfProcessing provides support for embedding [ZUGFeRD invoices]({%slug radpdfprocessing-embedded-file-streams-zugferd-invoices%}).

### Using the MergedEmbeddedFileNameResolving event

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
title: ZUGFeRD invoices
description: Learn how to embed a ZUGFeRD invoice into a PDF document utilizing the Telerik PdfProcessing library.
page_title: ZUGFeRD invoices
slug: radpdfprocessing-embedded-file-streams-zugferd-invoices
tags: pdf, file, stream, embedded, zugferd, invoice
position: 1
---

# ZUGFeRD invoices

|Minimum Version|Q4 2025|
|----|----|

[ZUGFeRD](https://de.wikipedia.org/wiki/ZUGFeRD) (acronym for Zentraler User Guide des Forums elektronische Rechnung Deutschland) is a specification for the electronic invoice format of the same name. **RadPdfProcessing** provides support for embedding of ZUGFeRD invoices.

### Creating an Embedded Electronic (ZUGFeRD) Invoice

#### **[C#] Add ZUGFeRD invoice**

<snippet id='pdf-add-zugferd-invoice'/>

>note Only a single XML invoice attachment is allowed according to the ZUGFeRD standard.

>important To comply with the PDF/A-3B standard all the fonts in the documents should be embedded, so please avoid using [Standard Fonts]({%slug radpdfprocessing-concepts-fonts%}) because they are not being embedded in the document. In **.NET Standard/.NET (Target OS: None)** environments, fonts beyond the [14 standard ones]({%slug radpdfprocessing-concepts-fonts%}#standard-fonts) require a [FontsProvider implementation]({%slug pdfprocessing-implement-fontsprovider%}) to be resolved correctly.

#### **[C#] Remove ZUGFeRD invoice**

<snippet id='pdf-remove-zugferd-invoice'/>

## ZugferdConformanceLevel

As of **Q4 2025** RadPdfProcessing provides support for specifying the ZUGFeRD (Factur-X) **conformance level** to use when exporting PDF invoices. Higher levels generally include all requirements of the lower levels and add more structured data to support automated processing and validation scenarios.

RadPdfProcessing offers the functionality to specify the **ZugferdConformanceLevel** when embedding the invoice. The available options are:

* **Minimum**: The minimal profile providing only the essential data needed for a compliant e-invoice. Suitable for simple use cases with limited automation.
* **Basic**: The basic profile providing core structured data for improved interoperability and basic automated processing between trading partners. This is the default value.
* **Comfort**: The comfort profile with richer structured content, typically aligned with common business requirements to enable advanced automation.
* **Extended**: The most comprehensive profile including extended data elements to cover advanced or industry-specific scenarios beyond the comfort profile.

<snippet id='pdf-specify-zugferd-conformance-level'/>


Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
title: ExpandableMemoryStream
page_title: ExpandableMemoryStream
description: Learn about the segmented, dynamically growing in-memory stream implementation used for large PDF processing scenarios.
slug: radpdfprocessing-formats-and-conversion-pdf-expandablememorystream
tags: pdf, memory, performance, stream, fixed, document, processing, dpl, format, expandablememorystream, large, file, size
published: True
position: 3
---

# ExpandableMemoryStream

## Overview

ExpandableMemoryStream is a segmented in‑memory stream optimized for large or many parallel PDF operations. Instead of resizing one big array, it grows by adding fixed‑size blocks only when needed. This keeps allocations smaller and steadier, helps the GC, and maintains predictable performance as documents scale. Compared to MemoryStream (which resizes one contiguous array and copies data on expansion), this segmented approach adds fixed blocks without copying existing bytes, reducing large reallocations and LOH pressure for very large or unpredictable workloads. The block‑based design grows incrementally without moving existing bytes and supports very large content sizes while keeping allocation behavior stable under parallel load.

## Why a Segmented Approach

Large PDF generation often needs a temporary buffer. A normal contiguous array may reallocate and copy data multiple times as it expands, increasing CPU work, peak memory, and pressure on the Large Object Heap (LOH). Avoiding large contiguous allocations lowers fragmentation, reduces garbage collection pauses, and scales better when size is unpredictable or workloads are bursty.

## How It Works

Data lives in equal‑sized blocks held in order. When more space is required a single new block is allocated, earlier blocks stay untouched. A position maps to (block index, offset). Growing exposes cleared bytes ready for writing. Shrinking lowers only the visible length and retains the blocks so later growth can reuse already allocated memory without new large allocations.

## When to Use

Use it when you need to:

- Build or merge large PDFs fully in memory before saving.
- Combine many pieces where the final size is unknown.
- Run multiple document builds in parallel and want steady, predictable allocations.
- Seek and rewrite parts of the buffered content without triggering array growth copies.

## Example

The following example shows two common ways to load a large PDF document into memory before further processing. The first approach constructs the stream directly from a byte array and passes an explicit segment size (bufferSize). The second approach creates an empty instance and copies a file stream into it. The constructor's second parameter (bufferSize) is optional and defaults to 1,000,000 bytes (1 MB). You can omit it unless you want a different segment size.

<snippet id='libraries-pdf-formats-and-conversion-expandablememorystream-implementation'/>

In both cases the segmented internal structure avoids reallocating a single large contiguous buffer, helping performance and memory stability for very large PDF files.


Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ The __Export()__ method of **TextFormatProvider** allows you to pass a **TextFor
RadFixedDocument document = CreateRadFixedDocument();

TextFormatProvider provider = new TextFormatProvider();
string text = provider.Export(document, settings);
string text = provider.Export(document, settings); // Obsolete since Q4 2025
string text = provider.Export(document, settings, TimeSpan.FromSeconds(10));
{{endregion}}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ Represents a collection of Action objects associated with a [RadFixedPage]({%slu
|OnPageOpen|Gets or sets the collection of actions triggered when the page is opened.|
|OnPageClose|Gets or sets the collection of actions triggered when the page is closed.|

The following example shows how to utilize the JavaScript Actions functionality showing an alert when the second page in a document is closed
The following example shows how to utilize the JavaScript Actions functionality showing an alert when the second page in a document is closed:

```csharp

Expand Down
Loading