Public/Test-VellumPdfConformance.ps1
|
function Test-VellumPdfConformance { <# .SYNOPSIS Validates a PDF against PDF/A-2 (b/u/a) or PDF/UA-1, in-process. .DESCRIPTION Runs VellumPdf's built-in preflight (VellumPdf.Conformance) on an existing PDF file or byte array - it does not need veraPDF or any external tool and works on any PDF, not just ones this module wrote. It returns one result object per profile with the verdict and every finding (rule id, ISO clause, severity, message, object). Without -Profile the PDF's own claim is read (its XMP PDF/A and PDF/UA identification) and every claimed profile is validated. A PDF that claims nothing needs an explicit -Profile. The preflight covers the checks that can be decided in-process; it is a fast regression gate, not a replacement for a certified validator when a formal compliance statement is required. Known gap: it does not examine fonts that are used only by form fields or other annotation appearances. Several files can be given (or piped); they are opened one at a time. A failure for one file (not found, unreadable, malformed, wrong password, ...) is a non-terminating error with that file as TargetObject and the next file is still processed; use -ErrorAction Stop to stop at the first one. Invalid parameters terminate. With -Quiet a file that could not be checked makes the result $false. .PARAMETER Path The PDF file(s). Accepts wildcards. Piped strings bind here, and so do piped objects with a Path property; the file returned by Save-VellumPdfDocument (a FileInfo) binds to -LiteralPath, so it can be piped straight in and names containing [ ] work. .PARAMETER LiteralPath The PDF file(s), taken literally (no wildcards). Aliases PSPath and FullName, so objects with either property (FileInfo, Select-Object FullName) bind by name. .PARAMETER Bytes The PDF as a byte array. .PARAMETER Profile PdfA2b, PdfA2u, PdfA2a or PdfUA1. Default: the profiles the PDF claims. .PARAMETER Password Password of an encrypted PDF (a SecureString). An empty user password is tried automatically. .PARAMETER Quiet Return only [bool]: $true when every validated profile passes. .EXAMPLE New-VellumPdfDocument -Conformance PdfA2b | Add-VellumPdfParagraph -Text 'x' | Save-VellumPdfDocument -Path ./a.pdf | Test-VellumPdfConformance .EXAMPLE Test-VellumPdfConformance ./report.pdf -Profile PdfUA1 | ForEach-Object Assertions | Where-Object Severity -eq Error .OUTPUTS PSVellumPDF.PreflightResult (Path, Profile, IsCompliant, ErrorCount, WarningCount, Assertions), or System.Boolean with -Quiet. #> [CmdletBinding(DefaultParameterSetName = 'Path')] [OutputType('PSVellumPDF.PreflightResult')] [OutputType([bool])] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidAssignmentToAutomaticVariable', '', Justification = '-Profile names the PDF conformance profile (documented API); inside this function it only shadows the host-profile variable.')] param( [Parameter(Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Path')] [string[]]$Path, [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'LiteralPath')] [Alias('PSPath', 'FullName')] [string[]]$LiteralPath, [Parameter(Mandatory, ParameterSetName = 'Bytes')] [AllowEmptyCollection()] [byte[]]$Bytes, [ValidateSet('PdfA2b', 'PdfA2u', 'PdfA2a', 'PdfUA1')] [string[]]$Profile, [securestring]$Password, [switch]$Quiet ) begin { try { $plain = if ($Password) { [System.Net.NetworkCredential]::new('', $Password).Password } $allOk = $true } catch { $PSCmdlet.ThrowTerminatingError((Resolve-VellumPdfErrorRecord -ErrorRecord $_ -CommandName 'Test-VellumPdfConformance')) } } process { try { foreach ($source in Get-VellumPdfInput -Path $Path -LiteralPath $LiteralPath -Bytes $Bytes -CommandName 'Test-VellumPdfConformance') { try { Read-VellumPdfInput -Source $source $name = $source.Name $data = [byte[]]$source.Data $profiles = $Profile if (-not $profiles) { try { $claimed = [VellumPdf.Conformance.PdfPreflight]::DetectClaimedProfiles($data, $plain) } catch [VellumPdf.Reader.PdfPasswordException] { throw (New-VellumPdfErrorRecord PasswordRequired "Test-VellumPdfConformance: '$name' is encrypted and needs a password. Pass -Password." -Target $name -InnerException $_.Exception) } catch [System.IO.InvalidDataException] { throw (New-VellumPdfErrorRecord InvalidPdf "Test-VellumPdfConformance: '$name' is not a well-formed PDF: $($_.Exception.Message)" -Target $name -InnerException $_.Exception) } $profiles = @(foreach ($c in $claimed) { switch ($c) { ([VellumPdf.Conformance.PdfConformance]::PdfA2B) { 'PdfA2b' } ([VellumPdf.Conformance.PdfConformance]::PdfA2U) { 'PdfA2u' } ([VellumPdf.Conformance.PdfConformance]::PdfA2A) { 'PdfA2a' } ([VellumPdf.Conformance.PdfConformance]::PdfUA1) { 'PdfUA1' } } }) if (-not $profiles) { throw ("Test-VellumPdfConformance: '$name' does not claim PDF/A-2 or PDF/UA-1 conformance. " + 'Pass -Profile to validate it against a profile anyway.') } } foreach ($p in $profiles) { $result = Invoke-VellumPdfPreflight -Bytes $data -Level $p -Password $plain -Source $name if (-not $result.IsCompliant) { $allOk = $false } if (-not $Quiet) { $result } } } catch { $allOk = $false # a file that could not be checked is not known to conform Write-VellumPdfFileError -Cmdlet $PSCmdlet -ErrorRecord (ConvertTo-VellumPdfFileError -ErrorRecord $_ -Target $source.Name -CommandName 'Test-VellumPdfConformance') -ErrorActionPreferenceValue $ErrorActionPreference } finally { $source.Data = $null; $data = $null } # one file in memory at a time } } catch { $PSCmdlet.ThrowTerminatingError((Resolve-VellumPdfErrorRecord -ErrorRecord $_ -CommandName 'Test-VellumPdfConformance')) } } end { try { if ($Quiet) { $allOk } } catch { $PSCmdlet.ThrowTerminatingError((Resolve-VellumPdfErrorRecord -ErrorRecord $_ -CommandName 'Test-VellumPdfConformance')) } } } |