Created
March 24, 2015 22:12
-
-
Save rionmonster/1072469d6b50e447d022 to your computer and use it in GitHub Desktop.
Continued Attempt at Dynamic Glyphs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using EnvDTE; | |
using EnvDTE80; | |
using Microsoft.VisualStudio.Shell; | |
using Microsoft.VisualStudio.Shell.Interop; | |
using Microsoft.CSS.Editor.Completion; | |
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel.Composition; | |
using System.Drawing; | |
using System.Drawing.Text; | |
using System.IO; | |
using System.Linq; | |
using System.Text.RegularExpressions; | |
using System.Windows.Media; | |
using System.Windows.Media.Imaging; | |
using TheArtOfDev.HtmlRenderer.WinForms; | |
namespace Glyphfriend.GlyphCompletionProviders | |
{ | |
[Export(typeof(ICssCompletionEntryGlyphProvider))] | |
class FontAwesomeCompletionEntryGlyphProvider : ICssCompletionEntryGlyphProvider | |
{ | |
// A flag to determine if the dynamic renderer has rendered the fonts for this library | |
private static bool _fontsInitialized = false; | |
// Store each of the byte[] data for each font for future calls (so that the fonts only have to be generated once) | |
private static Dictionary<string, byte[]> _fontMappings; | |
//private static BitmapFrame _icon = BitmapFrame.Create(new Uri("pack://application:,,,/WebEssentials2015;component/Resources/Images/foundation.png", UriKind.RelativeOrAbsolute)); | |
private static Regex _regex = new Regex(@"^font(-?)awesome(-.*)?(\.min)?\.css$", RegexOptions.IgnoreCase | RegexOptions.Compiled); | |
public ImageSource GetCompletionGlyph(string entryName, Uri sourceUri, CssNameType nameType) | |
{ | |
// If the source Uri for the image is null, ignore it | |
if (sourceUri == null) { return null; } | |
// Get the file path of the source being used | |
string filename = Path.GetFileName(sourceUri.ToString()).Trim(); | |
// Determine if this matches our filename | |
if (_regex.IsMatch(filename)) | |
{ | |
// It's a valid Font Awesome file, so generate the Glyphs | |
if (!_fontsInitialized) | |
{ | |
// Get the path for the current Project being targeted | |
var projectDirectoryPath = GetExecutingProjectDirectory(sourceUri.AbsolutePath); | |
// Initialize the Font Awesome icons for the renderer | |
InitializeFontAwesomeFonts(projectDirectoryPath); | |
} | |
// Check and initialize the glyph mappings from the CSS | |
if (_fontMappings == null) | |
{ | |
// Generate the mappings for each font | |
_fontMappings = ParseAndGenerateMappings(sourceUri.AbsolutePath); | |
} | |
// At this point, find the font that cooresponds to the existing Glyph and serve it | |
if (_fontMappings.ContainsKey(entryName)) | |
{ | |
return BitmapFrame.Create(new MemoryStream(_fontMappings[entryName])); | |
} | |
else | |
{ | |
// Otherwise serve the appropriate placeholder icon for this image | |
// return _icon; | |
} | |
} | |
return null; | |
} | |
private void InitializeFontAwesomeFonts(string projectDirectoryPath) | |
{ | |
// Instantiate all Font Awesome True Type fonts that are available (for the renderer) | |
// Build a collection of fonts | |
var fonts = new PrivateFontCollection(); | |
// Get the appropriate fonts that are present within this Project | |
foreach (var fontFamily in Directory.GetFiles(projectDirectoryPath, "*.ttf")) | |
{ | |
fonts.AddFontFile(fontFamily); | |
} | |
// Add each of the true type fonts to this renderer | |
foreach (var trueTypeFont in fonts.Families) | |
{ | |
HtmlRender.AddFontFamily(trueTypeFont); | |
} | |
_fontsInitialized = true; | |
} | |
private Dictionary<string, byte[]> ParseAndGenerateMappings(string path) | |
{ | |
// Read in the CSS from the file | |
var css = System.IO.File.ReadAllText(path); | |
// Read only the CSS Classes (only those with :before and beginning with .fa-) | |
var classes = Regex.Matches(css, @"\.[-]?([_a-zA-Z][_a-zA-Z0-9-]*):before|[^\0-\177]*\\[0-9a-f]{1,6}(\r\n[ \n\r\t\f])?|\\[^\n\r\f0-9a-f]*") | |
.Cast<Match>() | |
.Where(m => m.Value.StartsWith(".fa-")) | |
.Distinct(); | |
// Create a dictionary that maps class names to their respective unicode values | |
Dictionary<string, string> mappings = new Dictionary<string, string>(); | |
// Using those classes, grab the content value that appears within each of them, which will map | |
// the unicode values to the appropriate class | |
foreach (var cssClass in classes) | |
{ | |
// Find the cooresponding code for each class | |
var unicode = Regex.Match(css, cssClass + "[^\"]*\"(.*)\"[^}]*}"); | |
if (unicode != null && unicode.Groups.Count > 1) | |
{ | |
// Grab the match and store it | |
mappings.Add(cssClass.Value, String.Format("&#x{0};", unicode.Groups[1].Value.TrimStart('\\'))); | |
} | |
} | |
// At this point we have the unicode values that maps to each Glyph, so generate the byte[] | |
// for the images and store them | |
Dictionary<string, byte[]> glyphMappings = new Dictionary<string, byte[]>(); | |
// Loop through the available mappings and generate a Glyph for each | |
foreach (var mapping in mappings) | |
{ | |
// Generate an HTML Snippet to properly render the Glyph | |
var html = String.Format("<span style='font-family: FontAwesome; display:block;'>{0}</span>", mapping.Value); | |
glyphMappings.Add(mapping.Key, ImageToBytes(HtmlRender.RenderToImageGdiPlus(html, new Size(16, 16)))); | |
} | |
// Return all of these mappings | |
return glyphMappings; | |
} | |
private byte[] ImageToBytes(Image img) | |
{ | |
ImageConverter converter = new ImageConverter(); | |
return (byte[])converter.ConvertTo(img, typeof(byte[])); | |
} | |
// Resolve the appropriate path for this project | |
private string GetExecutingProjectDirectory(string filename) | |
{ | |
var dte2 = (DTE2)Package.GetGlobalService(typeof(DTE2)); | |
if (dte2 != null) | |
{ | |
var projItem = dte2.Solution.FindProjectItem(filename); | |
if (projItem != null) | |
{ | |
// If you have the project, figure out how to get it's root directory here | |
//return projItem.ContainingProject.GetThePathSomehow; | |
} | |
} | |
return null; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment