Update text truncation logic to use rendered width

Replaced character-based truncation with pixel-accurate truncation based on rendered width in inches. Updated `TruncateText` in `PDFBurner.vb` to use `Graphics.MeasureString` for width calculation and introduced a binary search for efficient truncation. Renamed `egName` to `fieldEgName` for clarity.

Replaced `TextMaxLengths` with `TextMaxWidths` in `PDFBurnerParams.vb`, specifying maximum render widths in inches. Updated default values for `position`, `city`, and `date`. Revised XML documentation to reflect the new truncation logic.
This commit is contained in:
2026-08-25 09:47:22 +02:00
parent feaea1626b
commit 35c1374b9c
2 changed files with 39 additions and 14 deletions

View File

@@ -311,20 +311,44 @@ Namespace Jobs.FinalizeDocument
#Region "Helpers" #Region "Helpers"
''' <summary> ''' <summary>
''' Truncates a field value to the configured max length for that egName. ''' Truncates a field value so its rendered width fits within the configured max width (inches).
''' Uses Graphics.MeasureString with the configured font for pixel-accurate measurement.
''' Appends TruncationSuffix when the value is cut. ''' Appends TruncationSuffix when the value is cut.
''' </summary> ''' </summary>
Private Function TruncateText(value As String, egName As String) As String Private Function TruncateText(value As String, fieldEgName As String) As String
If String.IsNullOrEmpty(value) Then Return value If String.IsNullOrEmpty(value) Then Return value
If _pdfBurnerParams.TextMaxLengths Is Nothing Then Return value If _pdfBurnerParams.TextMaxWidths Is Nothing Then Return value
Dim maxLen As Integer = 0 Dim maxWidthInches As Single = 0
If Not _pdfBurnerParams.TextMaxLengths.TryGetValue(egName, maxLen) Then Return value If Not _pdfBurnerParams.TextMaxWidths.TryGetValue(fieldEgName, maxWidthInches) Then Return value
If maxLen <= 0 OrElse value.Length <= maxLen Then Return value If maxWidthInches <= 0 Then Return value
Dim suffix As String = If(_pdfBurnerParams.TruncationSuffix, "") Dim suffix As String = If(_pdfBurnerParams.TruncationSuffix, "")
Dim cutAt As Integer = Math.Max(0, maxLen - suffix.Length)
Return value.Substring(0, cutAt) & suffix Using bmp As New Bitmap(1, 1)
Using g As Graphics = Graphics.FromImage(bmp)
Dim font As New Font(_pdfBurnerParams.FontName, _pdfBurnerParams.FontSize, _pdfBurnerParams.FontStyle, GraphicsUnit.Point)
Dim maxWidthPx As Single = maxWidthInches * g.DpiX
' Already fits — no truncation needed
If g.MeasureString(value, font).Width <= maxWidthPx Then Return value
' Binary search for the longest prefix that fits (with suffix)
Dim lo As Integer = 0
Dim hi As Integer = value.Length
While lo < hi
Dim mid As Integer = (lo + hi + 1) \ 2
Dim candidate As String = value.Substring(0, mid) & suffix
If g.MeasureString(candidate, font).Width <= maxWidthPx Then
lo = mid
Else
hi = mid - 1
End If
End While
Return value.Substring(0, lo) & suffix
End Using
End Using
End Function End Function

View File

@@ -20,15 +20,16 @@ Namespace Jobs.FinalizeDocument
Public Property TruncationSuffix As String = ".." Public Property TruncationSuffix As String = ".."
''' <summary> ''' <summary>
''' Per-field maximum character count (including the TruncationSuffix). ''' Per-field maximum render width in inches.
''' If a value exceeds the limit it is cut and TruncationSuffix is appended. ''' Text is measured using the configured Font/FontSize via Graphics.MeasureString (pixel-accurate).
''' If the rendered width exceeds the limit, the text is cut and TruncationSuffix is appended.
''' Keys must match egName values used in PDFBurner (e.g. "name", "position", "city", "date"). ''' Keys must match egName values used in PDFBurner (e.g. "name", "position", "city", "date").
''' Set a value to 0 to disable truncation for that field. ''' Set a value to 0 to disable truncation for that field.
''' </summary> ''' </summary>
Public Property TextMaxLengths As New Dictionary(Of String, Integer) From { Public Property TextMaxWidths As New Dictionary(Of String, Single) From {
{"position", 20}, {"position", 1.25F},
{"city", 20}, {"city", 1.25F},
{"date", 28} {"date", 2.0F}
} }
End Class End Class
End Namespace End Namespace