Public/Set-VellumPdfOutputIntent.ps1

function Set-VellumPdfOutputIntent {
    <#
    .SYNOPSIS
        Sets a custom ICC output intent on a PDF/A conformant VellumPdf document.
    .DESCRIPTION
        Wraps Document.SetPdfAOutputIntent(iccProfile, componentCount,
        outputConditionIdentifier, info) and the UseCmykOutputIntent convenience
        (the library's built-in generic CMYK profile). PDF/A documents embed an
        sRGB output intent by default; this cmdlet replaces it, which matters for
        archival workflows that mandate a specific output condition.

        REQUIRES PDF/A: the library only writes the output intent for conformant
        documents (it is silently ignored otherwise), so this cmdlet throws when
        the document's Conformance is None - create the document with
        New-VellumPdfDocument -Conformance PdfA2b (or another PDF/A level).

        The profile of an -IccProfilePath intent must be a printer, display, scanner or
        colour-space-conversion profile ('prtr', 'mntr', 'scnr', 'spac') of ICC major
        version 4 or lower; an abstract profile or a version 5 profile fails PDF/A
        (ISO 19005-2, 6.2.3) and is refused.

        RGB content drawn into a document whose output intent is CMYK fails the
        same validation (6.2.4.3), so the drawing cmdlets warn once per intent.

        CMYK CAVEAT: the layout engine renders text and table fills in DeviceRGB.
        A CMYK output intent is intended for documents that also carry CMYK
        content produced at the kernel level (not exposed by this module); strict
        PDF/A validators may flag DeviceRGB layout content combined with a pure
        CMYK output intent. Prefer a 3-component (RGB) profile for documents
        built with this module.

        The ICC profile bytes are embedded as-is; the library does not validate
        them at set time. Verify archival output with a PDF/A validator (e.g.
        veraPDF) as part of your workflow.
    .PARAMETER Document
        The live VellumPdf document flowing through the pipeline. The same
        instance is returned after the output intent is set, enabling chaining.
    .PARAMETER IccProfilePath
        Path to an ICC profile file (.icc/.icm) to embed as the output intent.
    .PARAMETER ComponentCount
        Optional. The profile's number of colour components (1 Gray, 3 RGB, 4 CMYK) is read
        from the profile itself; if you give it, it must match.
    .PARAMETER OutputConditionIdentifier
        The OutputConditionIdentifier string written to the OutputIntent
        dictionary (e.g. 'sRGB IEC61966-2.1'). Defaults to 'Generic CMYK' when
        -Cmyk is used.
    .PARAMETER Info
        Optional human-readable /Info string describing the output condition.
    .PARAMETER Cmyk
        Use the library's built-in generic CMYK ICC profile instead of supplying
        a profile file. See the CMYK caveat above.
    .EXAMPLE
        New-VellumPdfDocument -Conformance PdfA2b |
            Set-VellumPdfOutputIntent -IccProfilePath ./sRGB-v4.icc -ComponentCount 3 `
                -OutputConditionIdentifier 'sRGB v4 ICC preference' |
            Set-VellumPdfDocumentInfo -Title 'Archive' -Author 'Acme' |
            Add-VellumPdfParagraph -Text 'Archival body.' -FontHandle $font |
            Save-VellumPdfDocument -Path ./archive.pdf
    .EXAMPLE
        # Built-in generic CMYK intent (for documents that add CMYK kernel content)
        $doc | Set-VellumPdfOutputIntent -Cmyk
    .OUTPUTS
        VellumPdf.Layout.Document (the same instance, for chaining)
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '',
        Justification = 'Mutates an in-memory document object only; no external/system state change.')]
    [CmdletBinding(DefaultParameterSetName = 'IccProfile')]
    [OutputType([VellumPdf.Layout.Document], [VellumPdf.Document.PdfDocument])]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [object]$Document,

        [Parameter(Mandatory, Position = 0, ParameterSetName = 'IccProfile')]
        [string]$IccProfilePath,

        [Parameter(ParameterSetName = 'IccProfile')]
        [ValidateSet(1, 3, 4)]
        [int]$ComponentCount,

        [Parameter(Mandatory, ParameterSetName = 'IccProfile')]
        [Parameter(ParameterSetName = 'Cmyk')]
        [string]$OutputConditionIdentifier,

        [Parameter(ParameterSetName = 'IccProfile')]
        [string]$Info,

        [Parameter(Mandatory, ParameterSetName = 'Cmyk')]
        [switch]$Cmyk
    )

    process {
        try {
            Assert-VellumPdfDocumentOpen -Document $Document -CommandName 'Set-VellumPdfOutputIntent'

            # The library writes the output intent only for conformant documents
            # and silently ignores it otherwise - fail fast instead.
            if ($Document.Conformance -eq [VellumPdf.Document.PdfConformance]::None) {
                throw ('Set-VellumPdfOutputIntent: the output intent is only written for PDF/A conformant ' +
                    'documents. Create the document with New-VellumPdfDocument -Conformance PdfA2b ' +
                    '(or another PDF/A level) first.')
            }

            if ($PSCmdlet.ParameterSetName -eq 'Cmyk') {
                $identifier = if ($PSBoundParameters.ContainsKey('OutputConditionIdentifier')) {
                    $OutputConditionIdentifier
                }
                else {
                    'Generic CMYK'
                }
                $Document.UseCmykOutputIntent($identifier)
            }
            else {
                $resolved = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($IccProfilePath)
                if (-not [System.IO.File]::Exists($resolved)) {
                    throw (New-VellumPdfErrorRecord FileNotFound "Set-VellumPdfOutputIntent: ICC profile not found: '$resolved'.")
                }
                $bytes = [System.IO.File]::ReadAllBytes($resolved)
                $profileCount = Get-VellumPdfIccComponentCount -Bytes $bytes -CommandName 'Set-VellumPdfOutputIntent'
                Assert-VellumPdfIccOutputIntent -Bytes $bytes -CommandName 'Set-VellumPdfOutputIntent'
                if ($PSBoundParameters.ContainsKey('ComponentCount') -and $ComponentCount -ne $profileCount) {
                    throw ("Set-VellumPdfOutputIntent: -ComponentCount $ComponentCount does not match the profile, which has " +
                        "$profileCount colour component(s). Omit -ComponentCount to use the profile's own.")
                }
                $ComponentCount = $profileCount
                $infoValue = if ($PSBoundParameters.ContainsKey('Info')) { $Info } else { $null }
                $Document.SetPdfAOutputIntent($bytes, $ComponentCount, $OutputConditionIdentifier, $infoValue)
            }
            # Remember whether the (now final) intent is CMYK: PDF/A allows DeviceCMYK drawing only with one
            # (Add-VellumPdfShape -FillCmyk warns when it is missing) and DeviceRGB only without one
            # (RGB content then warns once). A later RGB profile clears the flag again.
            $isCmykIntent = $PSCmdlet.ParameterSetName -eq 'Cmyk' -or $ComponentCount -eq 4
            if ($isCmykIntent -and -not $Document.PSObject.Properties['PSVellumCmykIntent']) {
                $Document.PSObject.Properties.Add([psnoteproperty]::new('PSVellumCmykIntent', $true))
            }
            elseif (-not $isCmykIntent -and $Document.PSObject.Properties['PSVellumCmykIntent']) {
                $Document.PSObject.Properties.Remove('PSVellumCmykIntent')
            }
            if ($Document.PSObject.Properties['PSVellumRgbIntentWarned']) { $Document.PSObject.Properties.Remove('PSVellumRgbIntentWarned') }
            $Document
        }
        catch {
            $PSCmdlet.ThrowTerminatingError((Resolve-VellumPdfErrorRecord -ErrorRecord $_ -CommandName 'Set-VellumPdfOutputIntent'))
        }
    }
}