dotnet/wpf · error · ArgumentOutOfRangeException

SR.FaceIndexValidOnlyForTTC

Error message

SR.FaceIndexValidOnlyForTTC

What it means

SetFace allows switching faces only for TrueType Collection (.ttc) files. For single-face fonts (plain TTF/OTF), the only valid index is 0; any other index throws ArgumentOutOfRangeException with SR.FaceIndexValidOnlyForTTC.

Solutions

  1. Check the driver's font technology and only pass non-zero face indexes to .ttc files
  2. For single-face fonts always call SetFace(0) or skip the call
  3. Clamp/normalize the requested index: faceIndex = isCollection ? faceIndex : 0

Example fix

// before
driver.SetFace(requestedIndex); // crashes for .ttf with index 2
// after
if (driver.Technology == FontTechnology.TrueTypeCollection)
    driver.SetFace(requestedIndex);
else
    driver.SetFace(0);
Defensive patterns

Strategy: validation

Validate before calling

if (!isCollection && faceIndex != 0)
    throw new ArgumentException("faceIndex must be 0 for non-TTC fonts");

Try / catch

try { driver.SetFace(faceIndex); }
catch (ArgumentOutOfRangeException)
{
    driver.SetFace(0); // single-face fallback
}

Prevention

When it happens

Trigger: Calling SetFace(faceIndex) (directly or via ComputeSubset) on a TrueTypeFontDriver whose _technology is not TrueTypeCollection while faceIndex != 0.

Common situations: Subsetting or enumerating faces of a plain .ttf as if it were a .ttc, loop code that indexes faces without checking the font technology, tooling that assumes all font files contain multiple faces.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/22ab3a9f47a129a6. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/FontFace/FontDriver.cs:158

            }
            catch (ArgumentOutOfRangeException e)
            {
                // convert exceptions from CheckedPointer to FileFormatException
                throw new FileFormatException(SourceUri, e);
            }
        }

        internal void SetFace(int faceIndex)
        {
            if (_technology == FontTechnology.TrueTypeCollection)
            {
                ArgumentOutOfRangeException.ThrowIfNegative(faceIndex);
                ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(faceIndex, _numFaces);
            }
            else
            {
                if (faceIndex != 0)
                    throw new ArgumentOutOfRangeException(nameof(faceIndex), SR.FaceIndexValidOnlyForTTC);
            }

            try
            {
                CheckedPointer seekPosition = _fileStream + 4;

                if (_technology == FontTechnology.TrueTypeCollection)
                {
                    // this is a TTC file, we need to decode the ttc header

                    // skip version, num faces, OffsetTable array
                    seekPosition += (4 + 4 + 4 * faceIndex);

                    _directoryOffset = ReadOpenTypeLong(seekPosition);

                    seekPosition = _fileStream + (_directoryOffset + 4);
                    // 4 means that we skip the version number
                }

View on GitHub (pinned to 81131a70a4)