Exemple #1
0
            /// <summary>
            /// overall calculated line spacing
            /// </summary>
            static int Calculate_TypoMetricLineSpacing(TtfTypeface typeface)
            {
                //from https://www.microsoft.com/typography/OTSpec/recom.htm#tad
                //sTypoAscender, sTypoDescender and sTypoLineGap
                //sTypoAscender is used to determine the optimum offset from the top of a text frame to the first baseline.
                //sTypoDescender is used to determine the optimum offset from the last baseline to the bottom of the text frame.
                //The value of (sTypoAscender - sTypoDescender) is recommended to equal one em.
                //
                //While the OpenType specification allows for CJK (Chinese, Japanese, and Korean) fonts' sTypoDescender and sTypoAscender
                //fields to specify metrics different from the HorizAxis.ideo and HorizAxis.idtp baselines in the 'BASE' table,
                //CJK font developers should be aware that existing applications may not read the 'BASE' table at all but simply use
                //the sTypoDescender and sTypoAscender fields to describe the bottom and top edges of the ideographic em-box.
                //If developers want their fonts to work correctly with such applications,
                //they should ensure that any ideographic em-box values in the 'BASE' table describe the same bottom and top edges as the sTypoDescender and
                //sTypoAscender fields.
                //See the sections “OpenType CJK Font Guidelines“ and ”Ideographic Em-Box“ for more details.

                //For Western fonts, the Ascender and Descender fields in Type 1 fonts' AFM files are a good source of sTypoAscender
                //and sTypoDescender, respectively.
                //The Minion Pro font family (designed on a 1000-unit em),
                //for example, sets sTypoAscender = 727 and sTypoDescender = -273.

                //sTypoAscender, sTypoDescender and sTypoLineGap specify the recommended line spacing for single-spaced horizontal text.
                //The baseline-to-baseline value is expressed by:
                //OS/2.sTypoAscender - OS/2.sTypoDescender + OS/2.sTypoLineGap



                //sTypoLineGap will usually be set by the font developer such that the value of the above expression is approximately 120% of the em.
                //The application can use this value as the default horizontal line spacing.
                //The Minion Pro font family (designed on a 1000-unit em), for example, sets sTypoLineGap = 200.


                return(typeface.Ascender - typeface.Descender + typeface.LineGap);
            }
Exemple #2
0
            /// <summary>
            /// calculate Baseline-to-Baseline Distance (BTBD) for macOS
            /// </summary>
            /// <param name="typeface"></param>
            /// <returns>return 'unscaled-to-pixel' BTBD value</returns>
            static int CalculateBTBD_Mac(TtfTypeface typeface)
            {
                //from https://www.microsoft.com/typography/otspec/recom.htm#tad

                //Ascender and Descender are metrics defined by Apple
                //and are not to be confused with the Windows ascent or descent,
                //nor should they be confused with the true typographic ascender and descender that are found in AFM files.
                //The Macintosh metrics below are returned by the Apple Advanced Typography(AAT) GetFontInfo() API.
                //
                //
                //Macintosh Metric      OpenType Metric
                //ascender                  Ascender
                //descender                 Descender
                //leading                   LineGap

                //The suggested BTBD = ascent + descent + leading
                //If pixels extend above the ascent or below the descent,
                //the character will be squashed in the vertical direction
                //so that all pixels fit within these limitations; this is true for screen display only.

                //TODO: please test this
                HorizontalHeader hhea = typeface.HheaTable;

                return(hhea.Ascent + hhea.Descent + hhea.LineGap);
            }
Exemple #3
0
        /// <summary>
        /// get glyph layout plan or create if not exists
        /// </summary>
        /// <param name="typeface"></param>
        /// <param name="scriptLang"></param>
        /// <returns></returns>
        public GlyphLayoutPlanContext GetPlanOrCreate(TtfTypeface typeface, ScriptLang scriptLang)
        {
            GlyphLayoutPlanKey     key = new GlyphLayoutPlanKey(typeface, scriptLang.internalName);
            GlyphLayoutPlanContext context;

            if (!collection.TryGetValue(key, out context))
            {
                var glyphSubstitution = (typeface.GSUBTable != null) ? new GlyphSubstitution(typeface, scriptLang.shortname) : null;
                var glyphPosition     = (typeface.GPOSTable != null) ? new GlyphSetPosition(typeface, scriptLang.shortname) : null;
                collection.Add(key, context = new GlyphLayoutPlanContext(glyphSubstitution, glyphPosition));
            }
            return(context);
        }
Exemple #4
0
        public bool LoadTTF(Stream stream)
        {
            var reader = new OpenFontReader();

            ttfTypeFace = reader.Read(stream);
            if (ttfTypeFace != null)
            {
                this.ascent     = ttfTypeFace.Ascender;
                this.descent    = ttfTypeFace.Descender;
                this.unitsPerEm = ttfTypeFace.UnitsPerEm;
                return(true);
            }

            return(false);
        }
Exemple #5
0
            public static int CalculateLineSpacing(this TtfTypeface typeface, LineSpacingChoice choice)
            {
                switch (choice)
                {
                default:
                case LineSpacingChoice.Windows:
                    return(Calculate_BTBD_Windows(typeface));

                case LineSpacingChoice.Mac:
                    return(CalculateBTBD_Mac(typeface));

                case LineSpacingChoice.TypoMetric:
                    return(Calculate_TypoMetricLineSpacing(typeface));
                }
            }
Exemple #6
0
            public static bool RecommendToUseTypoMetricsForLineSpacing(this TtfTypeface typeface)
            {
                //https://www.microsoft.com/typography/otspec/os2.htm
                //
                //fsSelection ...
                //
                //bit     name
                //7       USE_TYPO_METRICS
                //
                //        Description
                //        If set, it is strongly recommended to use
                //        OS/2.sTypoAscender - OS/2.sTypoDescender + OS/2.sTypoLineGap
                //        as a value for default line spacing for this font.

                return(((typeface.OS2Table.fsSelection >> 7) & 1) != 0);
            }
Exemple #7
0
            /// <summary>
            /// calculate Baseline-to-Baseline Distance (BTBD) for Windows
            /// </summary>
            /// <param name="typeface"></param>
            /// <returns>return 'unscaled-to-pixel' BTBD value</returns>
            static int Calculate_BTBD_Windows(TtfTypeface typeface)
            {
                //from https://www.microsoft.com/typography/otspec/recom.htm#tad

                //Baseline to Baseline Distances
                //The 'OS/2' table fields sTypoAscender, sTypoDescender, and sTypoLineGap
                //free applications from Macintosh-or Windows - specific metrics
                //which are constrained by backward compatibility requirements.
                //
                //The following discussion only pertains to the platform-specific metrics.
                //The suggested Baseline to Baseline Distance(BTBD) is computed differently for Windows and the Macintosh,
                //and it is based on different OpenType metrics.
                //However, if the recommendations below are followed, the BTBD will be the same for both Windows and the Mac.

                //Windows Metric         OpenType Metric
                //ascent                    usWinAscent
                //descent                   usWinDescent
                //internal leading          usWinAscent + usWinDescent - unitsPerEm
                //external leading          MAX(0, LineGap - ((usWinAscent + usWinDescent) - (Ascender - Descender)))

                //The suggested BTBD = ascent + descent + external leading

                //It should be clear that the “external leading” can never be less than zero.
                //Pixels above the ascent or below the descent will be clipped from the character;
                //this is true for all output devices.

                //The usWinAscent and usWinDescent are values
                //from the 'OS/2' table.
                //The unitsPerEm value is from the 'head' table.
                //The LineGap, Ascender and Descender values are from the 'hhea' table.

                int usWinAscent       = typeface.OS2Table.usWinAscent;
                int usWinDescent      = typeface.OS2Table.usWinDescent;
                int internal_leading  = usWinAscent + usWinDescent - typeface.UnitsPerEm;
                HorizontalHeader hhea = typeface.HheaTable;
                int external_leading  = System.Math.Max(0, hhea.LineGap - ((usWinAscent + usWinDescent) - (hhea.Ascent - hhea.Descent)));

                return(usWinAscent + usWinDescent + external_leading);
            }
        public static void CollectAllAssociateGlyphIndex(this TtfTypeface typeface, List <ushort> outputGlyphIndexList, ScriptLang scLang, UnicodeLangBits[] selectedRangs = null)
        {
            //-----------
            //general glyph index in the unicode range

            //if user dose not specific the unicode lanf bit ranges
            //the we try to select it ourself.
            UnicodeLangBits[] unicodeLangBitsRanges;
            if (ScriptLangs.TryGenUnicodeLangBitsArray(scLang.shortname, out unicodeLangBitsRanges))
            {
                //one lang may contains may ranges
                if (selectedRangs != null)
                {
                    //select only in range
                    unicodeLangBitsRanges = FilterOnlySelectedRange(unicodeLangBitsRanges, selectedRangs);
                }

                foreach (UnicodeLangBits unicodeLangBits in unicodeLangBitsRanges)
                {
                    UnicodeRangeInfo rngInfo = unicodeLangBits.ToUnicodeRangeInfo();
                    int endAt = rngInfo.EndAt;
                    for (int codePoint = rngInfo.StartAt; codePoint <= endAt; ++codePoint)
                    {
                        ushort glyghIndex = typeface.LookupIndex(codePoint);
                        if (glyghIndex > 0)
                        {
                            //add this glyph index
                            outputGlyphIndexList.Add(glyghIndex);
                        }
                    }
                }
            }

            //-----------
            var gsub = new GlyphSubstitution(typeface, scLang.shortname);

            gsub.CollectAdditionalSubstitutionGlyphIndices(outputGlyphIndexList);
        }
Exemple #9
0
            public static bool DoesSupportUnicode(
                this TtfTypeface typeface,
                UnicodeLangBits unicodeLangBits)
            {
                if (typeface.OS2Table == null)
                {
                    return(false);
                }
                //-----------------------------
                long bits   = (long)unicodeLangBits;
                int  bitpos = (int)(bits >> 32);

                if (bitpos == 0)
                {
                    return(true); //default
                }
                else if (bitpos < 32)
                {
                    //use range 1
                    return((typeface.OS2Table.ulUnicodeRange1 & (1 << bitpos)) != 0);
                }
                else if (bitpos < 64)
                {
                    return((typeface.OS2Table.ulUnicodeRange2 & (1 << (bitpos - 32))) != 0);
                }
                else if (bitpos < 96)
                {
                    return((typeface.OS2Table.ulUnicodeRange3 & (1 << (bitpos - 64))) != 0);
                }
                else if (bitpos < 128)
                {
                    return((typeface.OS2Table.ulUnicodeRange4 & (1 << (bitpos - 96))) != 0);
                }
                else
                {
                    throw new System.NotSupportedException();
                }
            }
Exemple #10
0
 public static int CalculateRecommendLineSpacing(this TtfTypeface typeface, out LineSpacingChoice choice)
 {
     //check if we are on Windows env or macOS eve
     if (RecommendToUseTypoMetricsForLineSpacing(typeface))
     {
         choice = LineSpacingChoice.TypoMetric;
         return(Calculate_TypoMetricLineSpacing(typeface));
     }
     else
     {
         //check if we are on Windows or mac
         if (CurrentEnv.CurrentOSName == CurrentOSName.Mac)
         {
             choice = LineSpacingChoice.Mac;
             return(CalculateBTBD_Mac(typeface));
         }
         else
         {
             choice = LineSpacingChoice.Windows;
             return(Calculate_BTBD_Windows(typeface));
         }
     }
 }
 public GlyphSubstitution(TtfTypeface typeface, string lang)
 {
     _language          = lang;
     _typeface          = typeface;
     _mustRebuildTables = true;
 }
Exemple #12
0
            public static int CalculateRecommendLineSpacing(this TtfTypeface typeface)
            {
                LineSpacingChoice selectedChoice;

                return(CalculateRecommendLineSpacing(typeface, out selectedChoice));
            }
Exemple #13
0
 public void Clear()
 {
     _typeface = null;
     _glyphs.Clear();
 }
Exemple #14
0
 public GlyphLayoutPlanKey(TtfTypeface t, int scriptInternameName)
 {
     this.t = t;
     this.scriptInternameName = scriptInternameName;
 }
Exemple #15
0
        public GlyphSetPosition(TtfTypeface typeface, string lang)
        {
            this.Lang     = lang;
            this.typeface = typeface;
            //check if this lang has
            this.gposTable = typeface.GPOSTable;

            if (gposTable == null)
            {
                return;
            }

            ScriptTable scriptTable = gposTable.ScriptList[lang];

            //---------
            if (scriptTable == null)
            {
                return;
            }                                      // early exit if no lookup tables
                                                   //---------

            ScriptTable.LangSysTable defaultLang = scriptTable.defaultLang;
            if (defaultLang == null)
            {
                return;
            }                                      // early exit if no default language

            if (defaultLang.HasRequireFeature)
            {
            }
            //other feature
            if (defaultLang.featureIndexList != null)
            {
                //get features
                var features = new List <FeatureList.FeatureTable>();
                for (int i = 0; i < defaultLang.featureIndexList.Length; ++i)
                {
                    FeatureList.FeatureTable feature = gposTable.FeatureList.featureTables[defaultLang.featureIndexList[i]];

                    switch (feature.TagName)
                    {
                    case "mark":    //mark=> mark to base
                    case "mkmk":    //mkmk => mark to mask
                        //current version we implement this 2 features
                        features.Add(feature);
                        break;

                    case "kern":
                        //test with Candara font
                        features.Add(feature);
                        //If palt is activated, there is no requirement that kern must also be activated.
                        //If kern is activated, palt must also be activated if it exists.
                        //https://www.microsoft.com/typography/OTSpec/features_pt.htm#palt
                        break;

                    case "palt":

                        break;

                    default:
                        break;
                    }
                }

                //-----------------------

                lookupTables = new List <GPOS.LookupTable>();
                int j = features.Count;
                for (int i = 0; i < j; ++i)
                {
                    FeatureList.FeatureTable feature = features[i];
                    foreach (ushort lookupIndex in feature.LookupListIndices)
                    {
                        lookupTables.Add(gposTable.LookupList[lookupIndex]);
                    }
                }
            }
        }