2010-07-14 14 views
6

Tengo una aplicación de Silverlight que necesito para incrustar algunas fuentes menos comunes. Es lo suficientemente simple como para copiar el archivo TTF/OTF y compilarlo con mi aplicación. Sin embargo, en muchos casos, solo se usan de 5 a 10 caracteres. En otros casos, algunos archivos de fuentes son increíblemente grandes (Arial Unicode MS Regular tiene 22.1 MB, por ejemplo). Los tiempos de descarga rápidos de mi aplicación son realmente importantes, por lo que optimizar las fuentes utilizadas es primordial.Cómo crear fuentes subconjunto en .NET?

Por lo tanto, lo que estaba pensando es que he visto en aplicaciones como Expression Blend donde <Glyph/>is used to create a read-only font y también puede elegir incrustar solo ciertos caracteres. En otras circunstancias, he visto personas usar fuentes que solo contenían ciertos caracteres como un subconjunto de la fuente completa (y no usar un <Glyph/> en Silverlight, sino más bien usar el subtítulo .TTF como <FontFamily/>). Eso es amable de lo que estoy buscando, excepto que no estoy usando Expressions.

No estoy buscando soluciones furtivas, como exportar a un archivo XPS y tomar el archivo .odtff.

¿Existe una forma programática (.NET/GDI +) para crear un subconjunto de una fuente con solo ciertos caracteres y compilarlo en .TTF/.OTF? Además, esto también debería funcionar para los archivos .TTC.

Respuesta

3

Cambiando la respuesta aceptada a esta, ya que es .NET puro sin referencias externas. Utiliza .NET 4.0:

Imports System.Windows.Media 
Imports System.Text.Encoding 
Imports System.Collections 

Public Sub CreateSubSet(sourceText As String, fontURI As Uri) 
    Dim gt As FontEmbeddingManager = New FontEmbeddingManager 

    Dim glyphTypeface As GlyphTypeface = New GlyphTypeface(fontURI) 
    Dim Index As Generic.ICollection(Of UShort) 
    Index = New Generic.List(Of UShort) 
    Dim sourceTextBytes As Byte() = Unicode.GetBytes(sourceText) 
    Dim sourceTextChars As Char() = Unicode.GetChars(sourceTextBytes) 
    Dim sourceTextCharVal As Integer 
    Dim glyphIndex As Integer 
    For sourceTextCharPos = 0 To UBound(sourceTextChars) 
     sourceTextCharVal = AscW(sourceTextChars(sourceTextCharPos)) 
     glyphIndex = glyphTypeface.CharacterToGlyphMap(sourceTextCharVal) 
     Index.Add(glyphIndex) 
    Next 
    Dim filebytes() As Byte = glyphTypeface.ComputeSubset(Index) 
    Using fileStream As New System.IO.FileStream("C:\Users\Me\new-subset.ttf", System.IO.FileMode.Create) 
     fileStream.Write(filebytes, 0, filebytes.Length) 
    End Using 
End Sub 
+0

Me preguntaba para qué sirve la declaración de gt? No se usa después de haber sido declarado ... –

+0

@BrianTHOMAS, sí, lo siento, fue un artefacto de más código. no se usa en este ejemplo. siéntase libre de editar el código. –

5

La API nativa CreateFontPackage puede ser lo que estás buscando. Puede pasar un TTF y una lista de caracteres para mantener. Si pasa TTFCFP_SUBSET por usSubsetFormat, obtendrá un TTF que funcione con solo esos caracteres.

Here's a thread con lo que parece ser el código de un ejemplo de trabajo (en C, por desgracia).

+0

Esto es interesante, gracias por el enlace. Estaba esperando que algo estuviera en .NET, o al menos un ejemplo de usar esto desde .NET. –

+0

@Otaku: [Esta publicación] (http://social.msdn.microsoft.com/Forums/en/csharplanguage/thread/1652a9fb-87ab-4725-8a73-7f1015519a71) tiene el código P/Invoke necesario (consulte la segunda publicación) para usar la API en .NET. – josh3736

+0

Gracias Josh, voy a echar un buen vistazo a esto. –

5

En WPF para fuentes hay enlaces estáticos y dinámicos. Todo se puede definir en Blend. Con el enlace estático de fuentes, solo los caracteres necesarios se compilan e incrustan en su ensamblaje. Con la vinculación dinámica, todo el conjunto de fuentes está incrustado. Intente establecer enlaces estáticos para las fuentes seleccionadas e intente si funciona.

UPD

tratar de añadir el siguiente código en el archivo que .csproj. Aquí incluimos las fuentes Tahoma. La propiedad Autocompletar establecida en verdadero dice que vamos a incrustar en ensamblador solo los caracteres utilizados de nuestros controles. El conjunto de caracteres en el <Charachters/> etiqueta de punto de relleno para incluir estos caracteres en el ensamblaje. Todas las demás etiquetas se establecen en falso, porque no las necesitamos.

<ItemGroup> 
    <BlendEmbeddedFont Include="Fonts\tahoma.ttf"> 
     <IsSystemFont>True</IsSystemFont> 
     <All>False</All> 
     <AutoFill>True</AutoFill> 
     <Characters>dasf</Characters> 
     <Uppercase>False</Uppercase> 
     <Lowercase>False</Lowercase> 
     <Numbers>False</Numbers> 
     <Punctuation>False</Punctuation> 
    </BlendEmbeddedFont> 
    <BlendEmbeddedFont Include="Fonts\tahomabd.ttf"> 
     <IsSystemFont>True</IsSystemFont> 
     <All>False</All> 
     <AutoFill>True</AutoFill> 
     <Characters>dasf</Characters> 
     <Uppercase>False</Uppercase> 
     <Lowercase>False</Lowercase> 
     <Numbers>False</Numbers> 
     <Punctuation>False</Punctuation> 
    </BlendEmbeddedFont> 
    </ItemGroup> 
    <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> 

    <Import Project="$(MSBuildExtensionsPath)\Microsoft\Expression\Blend\3.0\WPF\Microsoft.Expression.Blend.WPF.targets" /> 
+0

Miré http://msdn.microsoft.com/en-us/library/ms753303.aspx y específicamente establece que WPF no tiene la capacidad de crear fuentes de subconjunto. Tampoco estoy usando Blend para nada, así que estoy buscando una manera de hacerlo programáticamente en .NET. –

+0

Ver la actualización. Si no usas Blend esto puede ayudarte (espero). –

+0

Esto parece * realmente * prometedor, de hecho, si pudiera hacerlo funcionar, esto sería exactamente lo que necesito. Esta página muestra una muestra: http://www.codeproject.com/KB/silverlight/NewIndianRupeeSymbolDemo.aspx. El ejemplo funciona, pero cuando intento hacerlo solo en un proyecto separado, falla. No obtengo un '' del IDE, pero incluso agregarlo manualmente no funciona. No estoy seguro de lo que estoy haciendo mal. –

0

FontForge (http://fontforge.sourceforge.net/) es un editor de fuentes de código abierto que permite conversiones de formato automatizados. Parece que solo es Python, pero podría valer la pena echarle un vistazo.

+0

¿Tiene alguna API para la creación de subconjuntos de fuentes? –

1

Sé que es una vieja pregunta, pero me ha parecido muy difícil utilizar la API de CreateFontPackage C# (como se ha mencionado por la respuesta de @ josh3736) así que pensé compartir mi código.

Estoy usando la API con los glyphIndices, puede usarla directamente con los caracteres eliminando el indicador TTFCFP_FLAGS_GLYPHLIST.

Este es mi código:

public byte[] CreateSubset(byte[] inputData, IEnumerable<ushort> glyphIndices) 
{ 
    AllocProc allocProc = Marshal.AllocHGlobal; 
    ReallocProc reallocProc = (p, c) => 
     p == IntPtr.Zero 
      ? Marshal.AllocHGlobal(c) 
      : Marshal.ReAllocHGlobal(p, c); 
    FreeProc freeProc = Marshal.FreeHGlobal; 

    var resultCode = CreateFontPackage(
     inputData, (uint) inputData.Length, 
     out var bufferPtr, 
     out _, 
     out var bytesWritten, 
     TTFCFP_FLAGS_SUBSET | TTFCFP_FLAGS_GLYPHLIST, 
     0, 
     TTFMFP_SUBSET, 
     0, 
     TTFCFP_MS_PLATFORMID, 
     TTFCFP_UNICODE_CHAR_SET, 
     glyphIndices, 
     (ushort)glyphIndices.Length, 
     allocProc, reallocProc, freeProc, (IntPtr)0); 

    if (resultCode != 0 || bufferPtr == IntPtr.Zero) 
    { 
     return null; 
    } 

    try 
    { 
     var buffer = new byte[bytesWritten]; 
     Marshal.Copy(bufferPtr, buffer, 0, buffer.Length); 
     return buffer; 
    } 
    finally 
    { 
     freeProc(bufferPtr); 
    } 
} 

internal const ushort TTFCFP_FLAGS_SUBSET = 0x0001; 
internal const ushort TTFCFP_FLAGS_COMPRESS = 0x0002; 
internal const ushort TTFCFP_FLAGS_TTC = 0x0004; 
internal const ushort TTFCFP_FLAGS_GLYPHLIST = 0x0008; 

internal const ushort TTFMFP_SUBSET = 0x0000; 

internal const ushort TTFCFP_UNICODE_PLATFORMID = 0x0000; 
internal const ushort TTFCFP_MS_PLATFORMID = 0x0003; 

internal const ushort TTFCFP_UNICODE_CHAR_SET = 0x0001; 

[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)] 
private delegate IntPtr AllocProc(Int32 size); 

[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)] 
private delegate IntPtr ReallocProc(IntPtr memBlock, IntPtr size); 

[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)] 
private delegate void FreeProc(IntPtr memBlock); 

[DllImport("FontSub.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] 
private static extern uint CreateFontPackage(
    [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] 
    byte[] puchSrcBuffer, 
    uint ulSrcBufferSize, 
    out IntPtr puchFontPackageBufferPtr, 
    out uint pulFontPackageBufferSize, 
    out uint pulBytesWritten, 
    ushort usFlags, 
    ushort usTtcIndex, 
    ushort usSubsetFormat, 
    ushort usSubsetLanguage, 
    ushort usSubsetPlatform, 
    ushort usSubsetEncoding, 
    [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 12)] 
    ushort[] pusSubsetKeepList, 
    ushort usSubsetKeepListCount, 
    AllocProc lpfnAllocate, 
    ReallocProc lpfnReAllocate, 
    FreeProc lpfnFree, 
    IntPtr lpvReserved 
); 

que se utiliza con código sólo con los archivos TTF, TTC (para colecciones de fuentes) que tiene que cambiar algunas cosas pero debería funcionar, no obstante.