PSImageConverter.psd1

#
# Module manifest for module 'PSImageConverter'
#
# Generated by: Ing. Fabian Steiner BSc.
#
# Generated on: 17.10.2026
#

@{

    # Script module or binary module file associated with this manifest.
    RootModule           = 'PSImageConverter.psm1'

    # Version number of this module.
    ModuleVersion        = '0.2.0'

    # Supported PSEditions
    CompatiblePSEditions = @('Core')

    # ID used to uniquely identify this module
    GUID                 = '285403f5-939e-438b-ad27-7395c4a4839f'

    # Author of this module
    Author               = 'Ing. Fabian Steiner BSc.'

    # Company or vendor of this module
    CompanyName          = 'Fabian Steiner'

    # Copyright statement for this module
    Copyright            = '(c) Ing. Fabian Steiner BSc. All rights reserved.'

    # Description of the functionality provided by this module
    Description          = 'Cross-platform PowerShell 7 module for converting images such as HEIC/HEIF photos to JPEG, PNG, WebP, AVIF and more, powered by Magick.NET; writes HEIC with the HEVC encoder of the operating system. Pipeline-friendly; keeps the EXIF metadata in JPEG, PNG, WebP, AVIF and JPEG XL output.'

    # Minimum version of the PowerShell engine required by this module
    PowerShellVersion    = '7.4'

    # Modules that must be imported into the global environment prior to importing this module
    RequiredModules      = @()

    # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
    FunctionsToExport    = @('Convert-Image','ConvertFrom-Heic','ConvertTo-Avif','ConvertTo-Heic','ConvertTo-Jpeg','ConvertTo-JpegXL','ConvertTo-Png','ConvertTo-WebP','Get-ImageInfo')

    # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
    CmdletsToExport      = @()

    # Variables to export from this module
    VariablesToExport    = @()

    # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
    AliasesToExport      = @()

    # DSC resources to export from this module
    DscResourcesToExport = @()

    # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
    PrivateData          = @{

        PSData = @{

            # Tags applied to this module. These help with module discovery in online galleries.
            Tags         = @('HEIC', 'HEIF', 'HEVC', 'Image', 'Conversion', 'JPEG', 'PNG', 'WebP', 'AVIF', 'JpegXL', 'ImageMagick', 'MagickNET', 'Windows', 'Linux', 'MacOS', 'PSEdition_Core')

            # A URL to the license for this module.
            LicenseUri   = 'https://www.gnu.org/licenses/gpl-3.0.html'

            # A URL to the main website for this project.
            ProjectUri   = 'https://github.com/fasteiner/PSImageConverter'

            # ReleaseNotes of this module
            ReleaseNotes = '## [0.2.0-preview0040] - 2026-10-18

### Added

- `Convert-Image`: Converts every image Magick.NET can read (HEIC/HEIF, JPEG, PNG, WebP, AVIF, TIFF, ...) to Jpeg, Png, WebP, Avif, Heic, Jxl (JPEG XL), Tiff, Bmp or Gif. `-Path` with wildcards, `-LiteralPath` (pipeline input from `Get-ChildItem` and `Get-ImageInfo`, folders are skipped, a file given twice is converted once), `-DestinationPath`, `-Quality` (lossy formats Jpeg, WebP, Avif, Heic, Jxl), `-Force`, `-WhatIf` and `-Confirm`. Applies the EXIF orientation (and then removes the EXIF thumbnail and updates the XMP orientation and pixel dimensions) and keeps the metadata the output format can carry unless `-StripMetadata` is used, which removes EXIF (including GPS), XMP, IPTC and comments but keeps the ICC colour profile (in PNG output only as iCCP chunk; PNG, JPEG and TIFF output of neutral grey pixels with an RGB profile is stored as RGB, so readers accept the profile). Warns when Tiff, Gif or Bmp output drops EXIF data. CMYK sources stay CMYK in Jpeg and Tiff output and are converted to sRGB through their profile for the other formats. Lossy Jxl output (`-Quality` below 100) stores no ICC profile, so the pixels of a source with one (Display P3, Adobe RGB) are converted to sRGB first and keep their look; `-Quality 100` keeps a profile that matches the pixels. An ICC profile that the colour engine cannot apply (damaged, e.g. cut short inside its tag data, or of an unknown colour space) is left out of every output, as viewers ignore it; before, PNG and lossless Jxl output failed with it and Chrome did not display the Avif. Grey images with a grey ICC profile keep it in Jpeg, Png, Tiff and lossless Jxl output and are converted to sRGB for WebP, Avif, Bmp and Gif, which store grey pixels as RGB; this includes a grey JPEG XL, which ImageMagick reads as RGB. A profile made for other pixels (a grey profile in a colour image, which viewers ignore, or an RGB profile on grey pixels, which browsers apply and Windows rejects) is written with unchanged pixels and kept, except that it is left out of lossless Jxl output, which could not be written with it, and, as a grey profile on colour pixels, out of Avif and Bmp output, which browsers would not show correctly, and out of Jpeg, Png and Tiff output of neutral grey pixels, which these formats store as grey. After a colour conversion, PNG output carries no `icc:*` text chunks naming the source profile. The colours are kept for sources with an ICC profile, as iPhone photos have; a HEIC or AVIF source that signals other primaries than sRGB only through nclx/CICP (e.g. Display P3, BT.2020) is converted without colour conversion. Transparent pixels are placed on white in Jpeg output (paper white for a CMYK image); the other formats keep the alpha channel, also Tiff output of a palette PNG with up to 16 colours. Sources with several frames (animated GIF/WebP, multi-page TIFF, HEIC with several images) are converted from their first frame with a warning; a source ImageMagick reports as damaged (e.g. a truncated JPEG, also one that lacks only its end marker although its pixels are complete, or an animated GIF cut off after its first frame) is converted with the warning `The image may be damaged (<reasons>): <path>`. Returns the written files as `Get-Item` does, so they can be piped into the next cmdlet also when their names contain `[` or `]`.
- `Convert-Image` writes through a temporary file in the target folder, so a failed or stopped (Ctrl+C) conversion leaves neither a truncated file nor the temporary file; file names with `%` and target names up to the file system limit work. It never overwrites the source file or a file written earlier in the same call (also not with `-Force`), compared by file identity, so a junction, a symbolic link, a subst or mapped drive, a UNC path, a short 8.3 name or a case-insensitive volume on Linux do not get around it; `-Force` replaces read-only and hidden files and keeps their attributes when the file cannot be replaced.
- `ConvertTo-Jpeg`, `ConvertTo-Png`, `ConvertTo-WebP`, `ConvertTo-Avif`, `ConvertTo-JpegXL`: Shortcuts for `Convert-Image -Format Jpeg/Png/WebP/Avif/Jxl` with the same parameters minus `-Format` (`ConvertTo-Png` has no `-Quality`). All files of one call, also from the pipeline, go through a single `Convert-Image` call, so duplicate targets are detected and `-WhatIf`, `-ErrorAction`, `-ErrorVariable`, `-OutVariable`, `-PipelineVariable` and `$?` behave like `Convert-Image`. A source already in the target format (e.g. `ConvertTo-Jpeg a.jpg`) needs a `-DestinationPath`; otherwise it gets a `TargetIsSource` error. The shortcuts call the module''s own `Convert-Image`, also when the session defines an alias or a function of that name.
- `ConvertTo-Heic` and `Convert-Image -Format Heic` (also `ConvertFrom-Heic -Format Heic`): write HEIC with the HEVC encoder of the operating system, because the bundled Magick.NET build has none and the module ships none (HEVC is covered by patent pools, and shipping an encoder would pass their licensing duties on to every user): on Windows 10 1809 or later the WIC HEIF encoder with the "HEVC Video Extensions" from the Microsoft Store, on Linux the libheif of the distribution (1.12 or later) with its HEVC encoder x265, built in or as a plugin (e.g. `sudo apt install libheif1 libheif-plugin-x265` on Ubuntu 24.04 and 26.04; the README and `about_PSImageConverter` list Debian, Fedora, Arch and Alpine; tested on all of them), on macOS ImageIO. The pixels are passed upright as 8-bit RGB with the ICC profile, the EXIF block with orientation 1 (set in place, so maker notes do not move) and the XMP packet, after the same colour steps as for the other formats (CMYK and grey pixels with a grey profile to sRGB, a grey profile on colour pixels left out); the bytes go through the temporary file like every other output. Windows and Linux keep EXIF and XMP as they are (also a block other programs cannot parse, as the other formats do) and the ICC profile; macOS writes no EXIF or XMP (with the EXIF warning) and keeps the ICC profile, except sRGB, which ImageIO stores as `nclx` colour information; ImageIO gets 32-bit pixels, because with 24-bit RGB it left the last column and row of an image with an odd width or height empty. Linux and macOS keep the alpha channel, Windows'' encoder takes opaque pixels only, so transparent pixels are placed on white as for Jpeg. On Windows every image is written as a grid of whole 512 × 512 tiles, padded by repeating its edge pixels and cropped back by a box that Windows'' own HEIF decoder also accepts for a side of 512 × k + 1 pixels (the encoder''s single-image mode crashed the process or wrote undecodable files for some sizes, and partly used tiles made the size of one image vary up to tenfold), and gets the `nclx` colour box the encoder leaves out (libheif readers showed pure red as 232, 0, 3). On Linux the colours are converted and signalled as BT.709 (`nclx` box and HEVC stream), so Windows'' own viewer shows them as libheif readers do, and an image is encoded as one HEVC slice, which Windows'' own viewer opens; only when that slice is larger than 15,000,000 bytes (Magick.NET decodes slices up to 16 MiB; a photo with much detail from about 25 megapixels, random noise from about 10) is it encoded again in as many slices as keep each below that size, at most 13 (x265 3.5 and 4.1 crash with 14 or more); libheif 1.17 and older (Ubuntu 22.04 and 24.04, Debian 12) store each of these slices as an extent of its own, which Windows'' own viewer refuses, so the module joins the extents into one, as libheif 1.21 and later store them; x265 gets an explicit thread pool for images larger than 8 megapixels and for images of several slices (x265 4.1 creates none of its own in containers with Docker''s default seccomp profile, where it crashed the process with several slices and encoded about five times more slowly); a codec plugin that libheif cannot load (on Arch Linux) does not hide its built-in HEVC encoder. Whether the encoder works is checked once per process with a small test image; when it does not, the call stops before any file is read, also with `-WhatIf`, with one terminating `HeicEncoderNotAvailable` error (category `NotInstalled`, target object `Heic`, reported by `ConvertTo-Heic` and `ConvertFrom-Heic` with their own name) that gives the reason, says what to install on this platform and suggests `-Format Avif`. An image the encoder rejects (on Windows a side longer than 22528 pixels, on Linux one longer than 32768, which libheif would not read back, or an image whose slices stay larger than 15,000,000 bytes in 13 slices) fails that file with `ImageWriteFailed`. On macOS the HEIC tests ran on the macOS 26 runner of GitHub Actions; which image sizes ImageIO rejects is not checked yet.
- `ConvertFrom-Heic`: Converts HEIC/HEIF images through `Convert-Image` (default format Jpeg) and rejects files without the extension `.heic`, `.heif` or `.hif`, except the files the call writes itself (`Get-ChildItem -Recurse` into a `-DestinationPath` inside the tree).
- All cmdlets report a failed file as non-terminating error with a stable error id (`PathNotFound`, `InvalidWildcardPattern`, `UnsupportedProvider`, `NotHeicFile`, `TargetIsSource`, `DuplicateTarget`, `TargetExists`, `ConfirmationFailed`, `ImageReadFailed`, `ImageWriteFailed`) and the source file as target object (for `PathNotFound`, `InvalidWildcardPattern` and `UnsupportedProvider` the input as given, one error per input): `$?` is `$false` after a failed file, `-ErrorVariable` holds one record per failure and `-ErrorAction Stop` stops at the first one. For a wildcard pattern that matches nothing, `PathNotFound` names a drive or folder that does not exist, a folder part that is a file, or a folder part with wildcards that no folder (or only a hidden one) matches, or, when the folder exists, says that wildcards skip hidden files and, for a pattern with `[` or `]`, that these are wildcard ch'


            # Prerelease string of this module
            Prerelease   = 'preview0040'

        } # End of PSData hashtable

    } # End of PrivateData hashtable

}