/// <summary>
        /// Create a FontFamilyIdentifier by concatenating two existing identifiers.
        /// </summary>
        internal FontFamilyIdentifier(FontFamilyIdentifier first, FontFamilyIdentifier second)
        {
            first.Canonicalize();
            second.Canonicalize();

            _friendlyName = null;
            _tokenCount   = first._tokenCount + second._tokenCount;
            _baseUri      = null;

            if (first._tokenCount == 0)
            {
                _canonicalReferences = second._canonicalReferences;
            }
            else if (second._tokenCount == 0)
            {
                _canonicalReferences = first._canonicalReferences;
            }
            else
            {
                _canonicalReferences = new CanonicalFontFamilyReference[_tokenCount];

                int i = 0;
                foreach (CanonicalFontFamilyReference family in first._canonicalReferences)
                {
                    _canonicalReferences[i++] = family;
                }
                foreach (CanonicalFontFamilyReference family in second._canonicalReferences)
                {
                    _canonicalReferences[i++] = family;
                }
            }
        }
Exemple #2
0
        /// <summary>
        /// Look up font family from canonical name
        /// </summary>
        /// <param name="canonicalName">font family canonical name</param>
        internal static IFontFamily LookupFontFamily(CanonicalFontFamilyReference canonicalName)
        {
            FontStyle   style   = FontStyles.Normal;
            FontWeight  weight  = FontWeights.Normal;
            FontStretch stretch = FontStretches.Normal;

            return(LookupFontFamilyAndFace(canonicalName, ref style, ref weight, ref stretch));
        }
Exemple #3
0
        /// <summary>
        /// Create font family from canonical family and ensure at least a
        /// fallback family is created if the specified name cannot be resolved.
        /// </summary>
        internal static IFontFamily SafeLookupFontFamily(
            CanonicalFontFamilyReference canonicalName,
            out bool nullFont
            )
        {
            nullFont = false;

            IFontFamily fontFamily = LookupFontFamily(canonicalName);

            if (fontFamily == null)
            {
                nullFont   = true;
                fontFamily = LookupFontFamily(NullFontFamilyCanonicalName);
                Invariant.Assert(fontFamily != null, "Unable to create null font family");
            }

            return(fontFamily);
        }
        internal void Canonicalize()
        {
            if (_canonicalReferences != null)
            {
                return;
            }

            int count = this.Count;

            if (count == 0)
            {
                return;
            }

            // First look up the entire friendly name in the cache; this may enable us to
            // save working set by sharing the same array of may equal FontFamilyIdentifier.
            BasedFriendlyName hashKey = new BasedFriendlyName(_baseUri, _friendlyName);

            CanonicalFontFamilyReference[] canonicalReferences = TypefaceMetricsCache.ReadonlyLookup(hashKey) as CanonicalFontFamilyReference[];

            if (canonicalReferences == null)
            {
                // We need to construct a new array.
                canonicalReferences = new CanonicalFontFamilyReference[count];

                // Add the first canonical family reference.
                int i, length;
                int j = FindToken(_friendlyName, 0, out i, out length);
                canonicalReferences[0] = GetCanonicalReference(i, length);

                // Add subsequent family references.
                for (int k = 1; k < count; ++k)
                {
                    j = FindToken(_friendlyName, j, out i, out length);
                    canonicalReferences[k] = GetCanonicalReference(i, length);
                }

                // Add the array to the cache.
                TypefaceMetricsCache.Add(hashKey, canonicalReferences);
            }

            // for thread safety, we assign to the field only after the array is fully initialized
            _canonicalReferences = canonicalReferences;
        }
        private CanonicalFontFamilyReference GetCanonicalReference(int startIndex, int length)
        {
            string normalizedString = Util.GetNormalizedFontFamilyReference(_friendlyName, startIndex, length);

            // For caching normalized names, we use a different type of key which does not compare equal
            // to the keys we used for caching friendly names.
            BasedNormalizedName hashKey = new BasedNormalizedName(_baseUri, normalizedString);

            // Look up the normalized string and base URI in the cache?
            CanonicalFontFamilyReference canonicalReference = TypefaceMetricsCache.ReadonlyLookup(hashKey) as CanonicalFontFamilyReference;

            // Do we already have a cached font family reference?
            if (canonicalReference == null)
            {
                // Not in cache. Construct a new font family reference.
                canonicalReference = CanonicalFontFamilyReference.Create(_baseUri, normalizedString);

                // Add it to the cache.
                TypefaceMetricsCache.Add(hashKey, canonicalReference);
            }

            return(canonicalReference);
        }
Exemple #6
0
        internal static IFontFamily LookupFontFamilyAndFace(
            CanonicalFontFamilyReference canonicalFamilyReference,
            ref FontStyle style,
            ref FontWeight weight,
            ref FontStretch stretch
            )
        {
            if (canonicalFamilyReference == null || object.ReferenceEquals(canonicalFamilyReference, CanonicalFontFamilyReference.Unresolved))
            {
                // no canonical name, e.g., because the friendly name was an empty string
                // or could not be canonicalized
                return(null);
            }

            try
            {
                FamilyCollection familyCollection;

                if (canonicalFamilyReference.LocationUri == null && canonicalFamilyReference.EscapedFileName == null)
                {
                    // No explicit location; use the default family collection.
                    familyCollection = _defaultFamilyCollection;
                }
                else if (canonicalFamilyReference.LocationUri != null)
                {
                    // Look in the location specified by the font family reference.
                    familyCollection = FamilyCollection.FromUri(canonicalFamilyReference.LocationUri);
                }
                else // canonicalFamilyReference.EscapedFileName != null
                {
                    // Look in the specified file in the Windows Fonts folder
                    // Note: CanonicalFamilyReference.EscapedFileName is safe to combine with Util.WindowsFontsUriObject because CanonicalFamilyReference guarantees that it will be a simple filename
                    // without relative path or directory components.
                    Uri locationUri = new Uri(Util.WindowsFontsUriObject, canonicalFamilyReference.EscapedFileName);
                    familyCollection = FamilyCollection.FromWindowsFonts(locationUri);
                }

                IFontFamily fontFamily = familyCollection.LookupFamily(
                    canonicalFamilyReference.FamilyName,
                    ref style,
                    ref weight,
                    ref stretch
                    );
                return(fontFamily);
            }
            // The method returns null in case of malformed/non-existent fonts and we fall back to the next font.
            // Therefore, we can disable PreSharp warning about empty catch bodies.
#pragma warning disable 6502
            catch (FileFormatException)
            {
                // malformed font file
            }
            catch (IOException)
            {
                // canonical name points to a place that doesn't exist or can't be read for some reason
            }
            catch (UnauthorizedAccessException)
            {
                // canonical name points to a place caller doesn't have permission to access
            }
            catch (ArgumentException)
            {
                // canonical name points to a valid Uri that doesn't point to a well formed
                // OS local path
            }
            catch (NotSupportedException)
            {
                // canonical name points to a Uri that specifies an unregistered scheme
            }
            catch (UriFormatException)
            {
                // canonical name points to a malformed Uri
            }
#pragma warning restore 6502
            // we want to fall back to the default fallback font instead of crashing
            return(null);
        }
        /// <summary>
        /// Maps characters to one of the font families in the specified FontFamilyList. This
        /// function differs from MapByFontFamilyList in that it returns as soon as at least
        /// one character is mapped; it does not keep going until it cannot map any more text.
        /// </summary>
        private int MapOnceByFontFamilyList(
            CharacterBufferRange unicodeString,
            CultureInfo culture,
            CultureInfo digitCulture,
            FontFamily[]                        familyList,
            ref PhysicalFontFamily firstValidFamily,
            ref int firstValidLength,
            IDeviceFont deviceFont,
            double scaleInEm,
            int recursionDepth,
            SpanVector scaledTypefaceSpans,
            int firstCharIndex,
            out int nextValid
            )
        {
            Invariant.Assert(familyList != null);

            int advance = 0;

            nextValid = 0;
            CharacterBufferRange mapString        = unicodeString;
            FontStyle            canonicalStyle   = _canonicalStyle;
            FontWeight           canonicalWeight  = _canonicalWeight;
            FontStretch          canonicalStretch = _canonicalStretch;

            // Note: FontFamilyIdentifier limits the number of family names in a single string. We
            // don't want to also limit the number of iterations here because if Typeface.FontFamily
            // has the maximum number of tokens, this should not prevent us from falling back to the
            // FallbackFontFamily (PS # 1148305).

            // Outer loop to loop over the list of FontFamily.
            for (int i = 0; i < familyList.Length; i++)
            {
                // grab the font family identifier and initialize the
                // target family based on whether it is a named font.
                FontFamilyIdentifier fontFamilyIdentifier = familyList[i].FamilyIdentifier;

                CanonicalFontFamilyReference canonicalFamilyReference = null;
                IFontFamily targetFamily;

                if (fontFamilyIdentifier.Count != 0)
                {
                    // Look up font family and face, in the case of multiple canonical families the weight/style/stretch
                    // may not match the typeface map's, since it is created w/ the first canonical family.
                    canonicalFamilyReference = fontFamilyIdentifier[0];
                    targetFamily             = FontFamily.LookupFontFamilyAndFace(canonicalFamilyReference, ref canonicalStyle, ref canonicalWeight, ref canonicalStretch);
                }
                else
                {
                    targetFamily = familyList[i].FirstFontFamily;
                }

                int familyNameIndex = 0;

                // Inner loop to loop over all name tokens of a FontFamily.
                for (;;)
                {
                    if (targetFamily != null)
                    {
                        advance = MapByFontFamily(
                            mapString,
                            culture,
                            digitCulture,
                            targetFamily,
                            canonicalFamilyReference,
                            canonicalStyle,
                            canonicalWeight,
                            canonicalStretch,
                            ref firstValidFamily,
                            ref firstValidLength,
                            deviceFont,
                            scaleInEm,
                            recursionDepth,
                            scaledTypefaceSpans,
                            firstCharIndex,
                            out nextValid
                            );

                        if (nextValid < mapString.Length)
                        {
                            // only strings before the smallest invalid needs to be mapped since
                            // string beyond smallest invalid can already be mapped to a higher priority font.
                            mapString = new CharacterBufferRange(
                                unicodeString.CharacterBuffer,
                                unicodeString.OffsetToFirstChar,
                                nextValid
                                );
                        }

                        if (advance > 0)
                        {
                            // found the family that shapes this string. We terminate both the
                            // inner and outer loops.
                            i = familyList.Length;
                            break;
                        }
                    }
                    else
                    {
                        // By definition null target does not map any of the input.
                        nextValid = mapString.Length;
                    }

                    if (++familyNameIndex < fontFamilyIdentifier.Count)
                    {
                        // Get the next canonical family name and target family.
                        canonicalFamilyReference = fontFamilyIdentifier[familyNameIndex];
                        targetFamily             = FontFamily.LookupFontFamilyAndFace(canonicalFamilyReference, ref canonicalStyle, ref canonicalWeight, ref canonicalStretch);
                    }
                    else
                    {
                        // Unnamed FontFamily or no more family names in this FontFamily.
                        break;
                    }
                }
            }

            nextValid = mapString.Length;
            return(advance);
        }
        /// <summary>
        /// Map characters by font family
        /// </summary>
        /// <remarks>
        /// Advance:
        ///     number of characters not mapped to missing glyph
        ///
        /// NextValid:
        ///     Offset to the nearest first character not mapped to missing glyph
        ///
        /// [Number of invalid characters following valid ones] = NextValid - Advance
        ///
        ///         A B C D E F G H x x x x x F G H I J
        ///         --------------->
        ///             Advance
        ///
        ///         ------------------------->
        ///                NextValid
        ///
        /// </remarks>
        private int MapByFontFamily(
            CharacterBufferRange unicodeString,
            CultureInfo culture,
            CultureInfo digitCulture,
            IFontFamily fontFamily,
            CanonicalFontFamilyReference canonicalFamilyReference,
            FontStyle canonicalStyle,
            FontWeight canonicalWeight,
            FontStretch canonicalStretch,
            ref PhysicalFontFamily firstValidFamily,
            ref int firstValidLength,
            IDeviceFont deviceFont,
            double scaleInEm,
            int recursionDepth,
            SpanVector scaledTypefaceSpans,
            int firstCharIndex,
            out int nextValid
            )
        {
            // This is the *one* place where we check for the font mapping depths of the font linking
            // process. This protects the linking process against extremely long chain of linking or
            // circular dependencies in the composite fonts.
            if (recursionDepth >= MaxTypefaceMapDepths)
            {
                // Stop the recursion. In effect, this FontFamily does not map any of the input.
                // Higher-level code must map the input text to some other FontFamily, or to the
                // "null font" if there is no valid FontFamily.
                nextValid = 0;
                return(0);
            }

            // If a device font is not already specified higher up the stack, look for a device font
            // for this font family that matches the typeface style, weight, and stretch.
            if (deviceFont == null)
            {
                deviceFont = fontFamily.GetDeviceFont(_canonicalStyle, _canonicalWeight, _canonicalStretch);
            }

            DigitMap digitMap = new DigitMap(digitCulture);

            int advance = 0;
            int cchAdvance;
            int cchNextValid;
            int ich = 0;

            nextValid = 0;

            bool terminated = false;

            while (ich < unicodeString.Length && !terminated)
            {
                // Determine length of run with consistent mapping. Start by assuming we'll be able to
                // use the whole string, then reduce to the length that can be mapped consistently.
                int cchMap = unicodeString.Length - ich;

                // Determine whether the run is using a device font, and limit the run to the
                // first boundary between device/non-device font usage.
                bool useDeviceFont = false;
                if (deviceFont != null)
                {
                    // Determine whether the first run uses a device font by inspecting the first character.
                    // We do not support device fonts for codepoints >= U+10000 (aka surrogates), so we
                    // don't need to call Classification.UnicodeScalar.
                    useDeviceFont = deviceFont.ContainsCharacter(digitMap[unicodeString[ich]]);

                    // Advance as long as 'useDeviceFont' remains unchanged.
                    int i = ich + 1;
                    while ((i < unicodeString.Length) &&
                           (useDeviceFont == deviceFont.ContainsCharacter(digitMap[unicodeString[i]])))
                    {
                        i++;
                    }

                    cchMap = i - ich;
                }


                // Map as many characters to a family as we can up to the limit (cchMap) just determined.
                string targetFamilyName;
                double mapSizeInEm;

                bool isCompositeFontFamily = fontFamily.GetMapTargetFamilyNameAndScale(
                    new CharacterBufferRange(
                        unicodeString,
                        ich,
                        cchMap
                        ),
                    culture,
                    digitCulture,
                    scaleInEm,
                    out cchMap,
                    out targetFamilyName,
                    out mapSizeInEm
                    );

                Debug.Assert(cchMap <= unicodeString.Length - ich);

                CharacterBufferRange mappedString = new CharacterBufferRange(
                    unicodeString,
                    ich,
                    cchMap
                    );


                if (!isCompositeFontFamily)
                {
                    // not a composite font family
                    cchAdvance = MapByFontFaceFamily(
                        mappedString,
                        culture,
                        digitCulture,
                        fontFamily,
                        canonicalStyle,
                        canonicalWeight,
                        canonicalStretch,
                        ref firstValidFamily,
                        ref firstValidLength,
                        useDeviceFont ? deviceFont : null,
                        false, // nullFont
                        mapSizeInEm,
                        scaledTypefaceSpans,
                        firstCharIndex + ich,
                        false, // ignoreMissing
                        out cchNextValid
                        );
                }
                else if (!string.IsNullOrEmpty(targetFamilyName))
                {
                    // The base Uri used for resolving target family names is the Uri of the composite font.
                    Uri baseUri = (canonicalFamilyReference != null) ? canonicalFamilyReference.LocationUri : null;

                    // map to the target of the family map
                    cchAdvance = MapByFontFamilyName(
                        mappedString,
                        culture,
                        digitCulture,
                        targetFamilyName,
                        baseUri,
                        ref firstValidFamily,
                        ref firstValidLength,
                        useDeviceFont ? deviceFont : null,
                        mapSizeInEm,
                        recursionDepth + 1, // increment the depth
                        scaledTypefaceSpans,
                        firstCharIndex + ich,
                        out cchNextValid
                        );
                }
                else
                {
                    // family map lookup returned no target family
                    cchAdvance   = 0;
                    cchNextValid = cchMap;
                }

                int cchValid   = cchMap;
                int cchInvalid = 0;

                cchValid   = cchAdvance;
                cchInvalid = cchNextValid;

                if (cchValid < cchMap)
                {
                    terminated = true;
                }

                advance  += cchValid;
                nextValid = ich + cchInvalid;

                ich += cchValid;
            }

            return(advance);
        }