Esempio n. 1
0
        public const string kInvalid    = "invalid";            // bad file format (eg, .ttc)

        /// <summary>
        /// On Window, we can use System.Windows.Media (which provides the GlyphTypeface class) to
        /// provide all the font metadata information.
        /// On Linux, we have to use Sharpfont from nuget (which provides the Sharpfont.Face class)
        /// for reading the font's embedding flag plus running /usr/bin/otfino for everything else.
        /// We get /usr/bin/otfinfo as part of the lcdf-typetools package that is specified in the
        /// debian/control file.
        /// </summary>
        public FontMetadata(string fontName, FontGroup group)
        {
            name          = fontName;
            fileExtension = Path.GetExtension(group.Normal);

            // First we detect invalid font types that we don't know how to handle (like .ttc)
            var bloomKnows = fontFileTypesBloomKnows.Contains(fileExtension.ToLowerInvariant());

            if (!bloomKnows)
            {
                determinedSuitability      = kInvalid;
                determinedSuitabilityNotes = "Bloom does not support " + fileExtension + " fonts.";
                return;
            }
#if __MonoCS__
            try
            {
                using (var lib = new SharpFont.Library())
                {
                    using (var face = new Face(lib, group.Normal))
                    {
                        var embeddingFlags = face.GetFSTypeFlags();

                        /*
                         * Quoting from the standard (https://docs.microsoft.com/en-us/typography/opentype/spec/os2#fstype)
                         *      Valid fonts must set at most one of bits 1, 2 or 3; bit 0 is permanently reserved and must be zero.
                         *      Valid values for this sub-field are 0, 2, 4 or 8. The meaning of these values is as follows:
                         *      0: Installable embedding
                         *      2: Restricted License embedding
                         *      4: Preview & Print embedding
                         *      8: Editable embedding
                         *
                         *      The specification for versions 0 to 2 did not specify that bits 0 to 3 must be mutually exclusive.
                         *      Rather, those specifications stated that, in the event that more than one of bits 0 to 3 are set in
                         *      a given font, then the least-restrictive permission indicated take precedence.
                         *      So we pay attention to RestrictedLicense only if neither Editable nor PreviewAndPrint is set.  (This
                         *      is apparently what Microsoft does in the GlyphTypeFace implementation.)
                         */
                        if ((embeddingFlags & (EmbeddingTypes.RestrictedLicense | EmbeddingTypes.Editable | EmbeddingTypes.PreviewAndPrint)) == EmbeddingTypes.RestrictedLicense)
                        {
                            fsType = "Restricted License";
                        }
                        else if ((embeddingFlags & EmbeddingTypes.BitmapOnly) == EmbeddingTypes.BitmapOnly)
                        {
                            fsType = "Bitmaps Only";
                        }
                        else if ((embeddingFlags & EmbeddingTypes.Editable) == EmbeddingTypes.Editable)
                        {
                            fsType = "Editable";
                        }
                        else if ((embeddingFlags & EmbeddingTypes.PreviewAndPrint) == EmbeddingTypes.PreviewAndPrint)
                        {
                            fsType = "Print and preview";
                        }
                        else
                        {
                            fsType = "Installable";                             // but is it really?
                        }
                        // Every call to face.GetSfntName(i) throws a null object exception.
                        // Otherwise the code would build on this fragment.
                        //var count = face.GetSfntNameCount();
                        //for (uint i = 0; i < count; ++i)
                        //{
                        //	try
                        //	{
                        //		var sfntName = face.GetSfntName(i);
                        //		...
                        //	}
                        //	catch (Exception ex)
                        //	{
                        //	}
                        //}
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("SharpLib threw an exception for {0}: {1}", group.Normal, ex);
                determinedSuitability      = kInvalid;
                determinedSuitabilityNotes = $"SharpLib exception: {ex}";
                return;
            }
            try
            {
                var process = new Process
                {
                    StartInfo = new ProcessStartInfo()
                    {
                        FileName               = "/usr/bin/otfinfo",
                        Arguments              = $"-i \"{group.Normal}\"",
                        UseShellExecute        = false,
                        CreateNoWindow         = true,
                        RedirectStandardOutput = true,
                        RedirectStandardError  = true,
                    },
                };
                process.Start();
                process.WaitForExit();
                var standardOutput = process.StandardOutput.ReadToEnd();
                var standardError  = process.StandardError.ReadToEnd();
                if (process.ExitCode == 0 && standardError.Length == 0)
                {
                    ParseOtfinfoOutput(standardOutput);
                }
                else
                {
                    Console.WriteLine("otfinfo -i \"{0}\" returned {1}.  Standard Error =\n{2}", group.Normal, process.ExitCode, standardError);
                    determinedSuitability      = kInvalid;
                    determinedSuitabilityNotes = $"otfinfo returned: {process.ExitCode}. Standard Error={Environment.NewLine}{standardError}";
                    return;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Process.Start() of otfinfo -i \"{0}\" threw an exception: {1}", group.Normal, e);
                determinedSuitability      = kInvalid;
                determinedSuitabilityNotes = $"Process.Start of \"otfinfo\" exception: {e}";
                return;
            }
#else
            GlyphTypeface gtf = null;
            try
            {
                gtf = new GlyphTypeface(new Uri("file:///" + group.Normal));
                var english = System.Globalization.CultureInfo.GetCultureInfo("en-US");
                version = gtf.VersionStrings[english];
                // Most fonts include the text "Version x" here, but our UI provides the
                // (possibly localized) text, so we strip it out here.
                if (version.StartsWith("Version "))
                {
                    version = version.Replace("Version ", "");
                }
                copyright = gtf.Copyrights[english];
                var embeddingRights = gtf.EmbeddingRights;
                switch (embeddingRights)
                {
                case FontEmbeddingRight.Editable:
                case FontEmbeddingRight.EditableButNoSubsetting:
                    fsType = "Editable";
                    break;

                case FontEmbeddingRight.Installable:
                case FontEmbeddingRight.InstallableButNoSubsetting:
                    fsType = "Installable";
                    break;

                case FontEmbeddingRight.PreviewAndPrint:
                case FontEmbeddingRight.PreviewAndPrintButNoSubsetting:
                    fsType = "Print and preview";
                    break;

                case FontEmbeddingRight.RestrictedLicense:
                    fsType = "Restricted License";
                    break;

                case FontEmbeddingRight.EditableButNoSubsettingAndWithBitmapsOnly:
                case FontEmbeddingRight.EditableButWithBitmapsOnly:
                case FontEmbeddingRight.InstallableButNoSubsettingAndWithBitmapsOnly:
                case FontEmbeddingRight.InstallableButWithBitmapsOnly:
                case FontEmbeddingRight.PreviewAndPrintButNoSubsettingAndWithBitmapsOnly:
                case FontEmbeddingRight.PreviewAndPrintButWithBitmapsOnly:
                    fsType = "Bitmaps Only";
                    break;
                }
                designer        = gtf.DesignerNames[english];
                designerURL     = gtf.DesignerUrls[english];
                license         = gtf.LicenseDescriptions[english];
                manufacturer    = gtf.ManufacturerNames[english];
                manufacturerURL = gtf.VendorUrls[english];
                trademark       = gtf.Trademarks[english];
            }
            catch (Exception e)
            {
                // file is somehow corrupt or not really a font file? Just ignore it.
                Console.WriteLine("GlyphTypeface for \"{0}\" threw an exception: {1}", group.Normal, e);
                determinedSuitability      = kInvalid;
                determinedSuitabilityNotes = $"GlyphTypeface exception: {e}";
                return;
            }
#endif
            variants = group.GetAvailableVariants().ToArray();

            // Now for the hard part: setting DeterminedSuitability
            // Check out the license information.
            if (!String.IsNullOrEmpty(license))
            {
                if (license.Contains("Open Font License") || license.Contains("OFL") ||
                    license.StartsWith("Licensed under the Apache License") ||
                    license.Contains("GNU GPL") || license.Contains("GNU General Public License") || license.Contains(" GPL ") || license.Contains(" GNU ") ||
                    (license.Contains("GNU license") && license.Contains("www.gnu.org")) ||
                    license.Contains("GNU LGPL") || license.Contains("GNU Lesser General Public License"))
                {
                    determinedSuitability = kOK;
                    if (license.Contains("Open Font License") || license.Contains("OFL"))
                    {
                        determinedSuitabilityNotes = "Open Font License";
                    }
                    else if (license.StartsWith("Licensed under the Apache License"))
                    {
                        determinedSuitabilityNotes = "Apache License";
                    }
                    else if (license.Contains("GNU LGPL") || license.Contains("GNU Lesser General Public License"))
                    {
                        determinedSuitabilityNotes = "GNU LGPL";
                    }
                    else
                    {
                        determinedSuitabilityNotes = "GNU GPL";
                    }
                    return;
                }
                if (license.Replace('\n', ' ').Contains("free of charge") && license.Contains("Bitstream"))
                {
                    determinedSuitability      = kOK;
                    determinedSuitabilityNotes = "Bitstream free license";
                    return;
                }
            }
            if (licenseURL == "http://dejavu-fonts.org/wiki/License")
            {
                determinedSuitability      = kOK;
                determinedSuitabilityNotes = "Bitstream free license";
                return;
            }
            if (!String.IsNullOrEmpty(copyright))
            {
                // some people put license information in the copyright string.
                if (copyright.Contains("Artistic License"))
                {
                    determinedSuitability      = kOK;
                    determinedSuitabilityNotes = "Artistic License";
                    return;
                }
                if (copyright.Contains("GNU General Public License") || copyright.Contains(" GPL "))
                {
                    determinedSuitability      = kOK;
                    determinedSuitabilityNotes = "GNU GPL";
                    return;
                }
                if (copyright.Contains("SIL Open Font License"))
                {
                    determinedSuitability      = kOK;
                    determinedSuitabilityNotes = "Open Font License";
                    return;
                }
                if (copyright.Contains("Ubuntu Font Licence"))                  // British spelling I assume...
                {
                    determinedSuitability      = kOK;
                    determinedSuitabilityNotes = "Ubuntu Font Licence";
                    return;
                }
            }
            if (fsType == "Restricted License" || fsType == "Bitmaps Only")
            {
                determinedSuitability      = kUnsuitable;
                determinedSuitabilityNotes = "unambiguous fsType value";
                return;
            }
            if (manufacturer == "Microsoft Corporation" || manufacturer == "Microsoft Corp." ||
                (license != null && license.Contains("Microsoft supplied font") && manufacturer != null && manufacturer.Contains("Monotype")) ||
                (license == null && manufacturer == null &&
                 copyright != null && copyright.Contains("Microsoft Corporation") &&
                 trademark != null && trademark.Contains("is a trademark of Microsoft Corporation"))
                )
            {
                // Review what about "Print and Preview"?
                determinedSuitability      = kOK;
                determinedSuitabilityNotes = "fsType from reliable source";
                return;
            }
            // Give up.  More heuristics may suggest themselves.
            determinedSuitability      = kUnknown;
            determinedSuitabilityNotes = "no reliable information";
        }
Esempio n. 2
0
        /// <summary>
        /// Note: There is some performance overhead to initializing this.
        /// </summary>
        private void InitializeFontData()
        {
            FontNameToFiles    = new Dictionary <string, FontGroup>();
            FontsWeCantInstall = new HashSet <string>();
#if __MonoCS__
            using (var lib = new SharpFont.Library())
            {
                // Find all the font files in the standard system location (/usr/share/font) and $HOME/.local/share/fonts (if it exists)
                foreach (var fontFile in FindLinuxFonts())
                {
                    try
                    {
                        using (var face = new SharpFont.Face(lib, fontFile))
                        {
                            var embeddingTypes = face.GetFSTypeFlags();
                            if ((embeddingTypes & EmbeddingTypes.RestrictedLicense) == EmbeddingTypes.RestrictedLicense ||
                                (embeddingTypes & EmbeddingTypes.BitmapOnly) == EmbeddingTypes.BitmapOnly)
                            {
                                // Our font UI allows any font on the computer, but gives the user indications that some are more
                                // useable in publishing Bloom books. The NoteFontsWeCantInstall prop is only true when we call this
                                // from BloomPubMaker so that it can note that certain fonts are unsuitable for embedding in ePUBs.
                                if (NoteFontsWeCantInstall)
                                {
                                    FontsWeCantInstall.Add(face.FamilyName);
                                    continue;
                                }
                            }
                            var name = face.FamilyName;
                            // If you care about bold, italic, etc, you can filter here.
                            FontGroup files;
                            if (!FontNameToFiles.TryGetValue(name, out files))
                            {
                                files = new FontGroup();
                                FontNameToFiles[name] = files;
                            }
                            files.Add(face, fontFile);
                        }
                    }
                    catch (Exception)
                    {
                        continue;
                    }
                }
            }
#else
            foreach (var fontFile in FindWindowsFonts())
            {
                GlyphTypeface gtf;
                try
                {
                    gtf = new GlyphTypeface(new Uri("file:///" + fontFile));
                }
                catch (Exception)
                {
                    continue;                     // file is somehow corrupt or not really a font file? Just ignore it.
                }

                // Our font UI allows any font on the computer, but gives the user indications that some are more
                // useable in publishing Bloom books. The NoteFontsWeCantInstall prop is only true when we call this
                // from BloomPubMaker so that it can note that certain fonts are unsuitable for embedding in ePUBs.
                if (!FontIsEmbeddable(gtf.EmbeddingRights) && NoteFontsWeCantInstall)
                {
                    string name1 = GetFontNameFromFile(fontFile);
                    if (name1 != null)
                    {
                        FontsWeCantInstall.Add(name1);
                    }
                    continue;                     // not allowed to embed in ePUB
                }

                string name = GetFontNameFromFile(fontFile);
                if (name == null)
                {
                    continue;                     // not sure how this can happen but I've seen it.
                }
                // If you care about bold, italic, etc, you can filter here.
                FontGroup files;
                if (!FontNameToFiles.TryGetValue(name, out files))
                {
                    files = new FontGroup();
                    FontNameToFiles[name] = files;
                }
                files.Add(gtf, fontFile);
            }
#endif
        }