Private/New-VellumTextStyle.ps1
|
function New-VellumTextStyle { <# .SYNOPSIS Builds a VellumPdf.Layout.Core.TextStyle from simple scalar parameters. .DESCRIPTION Internal helper shared by the public Add-* functions so that font, size, color, and alignment handling stays in one place. Returns $null when no styling was requested, letting callers fall back to the document's default font. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Returns a new in-memory TextStyle object; performs no external/system state change.')] [CmdletBinding()] [OutputType([VellumPdf.Layout.Core.TextStyle])] param( [string]$Font, [double]$FontSize, [VellumPdf.Fonts.EmbeddedFontHandle]$FontHandle, [ValidateCount(3, 3)] [ValidateRange(0.0, 1.0)] [double[]]$Color, [string]$LinkUri, # Line leading (extra vertical space between lines), in points. [ValidateRange(0, 1000)] [double]$Leading ) $wantsColor = $PSBoundParameters.ContainsKey('Color') $wantsLeading = $PSBoundParameters.ContainsKey('Leading') # Link hygiene: whitespace-only URIs become no-link (a literal '/URI ( )' # annotation otherwise lands in the PDF), and the URI scheme is allowlisted # to http/https/mailto. An allowlist (not a blocklist) is used so a NEW # dangerous scheme - a future protocol handler, vbscript:, file:, data:, or a # scheme-relative '//host' that a reader resolves as https - cannot reach the # PDF just because it is not on a list of known-bad names. A generated /URI # action should only ever be an outbound web or mail link. # # The scheme is read from a copy with ALL whitespace and control characters # removed, not just leading ones, because lenient readers strip that noise # before dispatching the scheme. Reading the scheme from the raw value would # let 'java<TAB>script:', a mid-keyword no-break space, or a leading 0x01 # byte present a different scheme to the reader than the one we validated. $LinkUri = if ($PSBoundParameters.ContainsKey('LinkUri')) { $LinkUri.Trim() } else { '' } $wantsLink = $LinkUri -ne '' if ($wantsLink) { $normalized = $LinkUri -replace '[\s\p{Cc}\p{Cf}]', '' $scheme = [regex]::Match($normalized, '^(?<s>[a-zA-Z][a-zA-Z0-9+.-]*):').Groups['s'].Value if ($scheme.ToLowerInvariant() -notin @('http', 'https', 'mailto')) { $what = if ($scheme) { "scheme '$scheme'" } else { 'a relative or scheme-less URI' } throw ("-LinkUri uses $what; only http, https, and mailto URLs are allowed in " + "generated documents (got '$LinkUri').") } } if (-not $Font -and -not $PSBoundParameters.ContainsKey('FontSize') -and -not $FontHandle ` -and -not $wantsColor -and -not $wantsLink -and -not $wantsLeading) { return $null } $style = [VellumPdf.Layout.Core.TextStyle]::new() if ($FontHandle) { # Embedded TrueType font wins; do NOT also set Standard14 Font. $style.FontRef = [VellumPdf.Layout.Core.FontReference]::new($FontHandle) } elseif ($Font) { # Standard14 exposes one static field per base-14 font (Helvetica, etc.). $style.Font = [VellumPdf.Fonts.Standard14]::$Font } if ($PSBoundParameters.ContainsKey('FontSize')) { $style.FontSize = $FontSize } if ($wantsColor) { $style.Color = [VellumPdf.Layout.Core.ColorRgb]::new($Color[0], $Color[1], $Color[2]) } if ($wantsLink) { $style.LinkUri = $LinkUri } if ($wantsLeading) { $style.Leading = $Leading } return $style } |