2011-01-25 14 views
7

So come creare un selettore di font WPF con alcune righe di XAML, che si lega a Fonts.SystemFontFamilies (thanks to Norris Cheng's excellent blog post), ma non riesco a capire come filtrare tutte le famiglie di caratteri internazionali e di altri caratteri non romani. Non è probabile che i miei utenti abbiano bisogno di "Baiti mongoli", "Microsoft Uighur" o anche "Webdings", e quando questi sono nella lista rende più difficile per loro trovare i caratteri che desiderano .WPF: come filtrare i caratteri non romani da Fonts.SystemFontFamilies?

Ci sono proprietà sugli oggetti raggiungibili da SystemFontFamilies che posso utilizzare per separare le famiglie di caratteri di alfabeto romano non simbolo dal resto?

MODIFICA: Le informazioni che desidero utilizzare sono disponibili nel pannello di controllo di Windows 7 Font, nel campo "Progettato per". Le stringhe in questo campo contengono "Latin" per i caratteri utili per le applicazioni dell'alfabeto latino, altri nomi di lingua per i caratteri "internazionali" ("Mongoli Baiti" è "mongolo", ad esempio) e "Simbolo" per i caratteri come Wingdings. Il campo "Categoria" sembra utile anche per separare i caratteri di "visualizzazione" dai caratteri "di testo". Sfortunatamente, non riesco a trovare un modo per ottenere queste informazioni dal codice e non riesco nemmeno a capire dove si trova nello OpenType spec. La mia ipotesi è che siano coinvolti OpenType script tags o language tags.

+0

Il filtraggio può essere fatto con la 'evento filter' del' CollectionViewSource' ma immagino si sapeva che. Non ho mai visto un modo per scoprire se FontFamily è Symbol, ecc. Interessante vedere cosa ti viene in mente –

+1

Sei sicuro che lo rende un * lotto * più difficile, o che si tratta solo di un piccolo inconveniente molto occasionale. Anche se rimuovi i caratteri "non romani" ci saranno ancora molti font. Domanda interessante però: +1. –

+0

Gli script supportati da un font non sono disponibili tramite le classi di font correlate. Sei sicuro che questo sia costantemente disponibile tramite i metadati dei font incorporati e che MS non abbia eseguito solo un'analisi dei font per i font noti per Windows 7 per creare un DB di metadati dei font? –

risposta

1

Non penso che riuscirai mai a trovare quel tipo di informazioni senza fornirle tu stesso. Le specifiche dei caratteri di per sé non forniscono queste informazioni, quindi immagino che questo significhi essere sfortunati. La soluzione migliore è determinare un elenco di caratteri che ritieni "accettabili" per i tuoi utenti finali.

+0

Questa è la strada che probabilmente finirò per prendere, dopo che la mia tolleranza per perdere tempo su dettagli un po 'irrilevanti è esaurita. Mi dà fastidio, però, perché so che le informazioni sono lì da qualche parte - vedi la mia domanda modificata. – McKenzieG1

+0

Hmmm .. Questa è un'informazione interessante. Ora sono curioso Mi chiedo però se si tratta di informazioni opzionali su un font, e se si applica anche a formati diversi da OpenType. Posso immaginare di scivolare attraverso la rete, se questo è il caso. Sto solo dicendo –

11

Non c'è alcuna informazione esplicita in un font che definisce in modo univoco se è leggibile come "Roman" o meno.

È possibile ricorrere all'analisi del glifo per vedere quale intervallo Unicode copre un carattere. Questo potrebbe darti un'idea di quali lingue è coperto da un font. Tuttavia, questo ha anche problemi, ad esempio

  1. Il tipo di carattere predefinito di Windows 7 è "Segoe UI". Nel tuo schema vedresti questo come un font "romano"? Il problema qui è che anche se esegui l'analisi del glifo, copre il latino ma anche altri intervalli Unicode, ad es. Arabo e tailandese. Ok, possiamo includere caratteri che almeno coprono il latino, tuttavia, e se la gamma latina non è effettivamente leggibile come latino come nel punto 3?
  2. L'esempio di "Baiti mongoli" include glifi che coprono la gamma latina di base, quindi può essere utilizzato per rendere il testo "romano".
  3. Webdings copre la gamma latina, quindi tramite analisi potrebbe passare, ma in realtà non contiene caratteri latini leggibili.

L'analisi di glifi potrebbe essere applicata per restringere le cose, forse, ma si potrebbero ottenere falsi positivi.

Aggiornamento

ho a che fare con le liste dei font nella mia propria applicazione, quindi non potevo lasciare questo uno solo! :)

In realtà è possibile derivare se un carattere è un carattere simbolo tramite la proprietà GlyphTypeface.Symbol (che è nuovo per me). Pertanto con questo e un po 'di analisi del glifo, la seguente soluzione dovrebbe fare il trucco.

Tuttavia troverà ancora "Baiti mongoli" (ed è "Baiti" non "Balti" come in stile curry :)) come questo ha glifi per caratteri latini quindi è ancora un carattere "romano" a seconda di come lo definisci. Di fatto, tutti i caratteri non Symbol sul mio sistema hanno almeno il range di caratteri latini, quindi il test del glifo latino non esclude alcun carattere.

Qual è la tua particolare obiezione a "Baiti mongoli" e come prevedi di escluderla automaticamente (senza utilizzare un elenco di esclusione gestito manualmente, ad esempio)?

[Test] 
public void test() 
{ 
    var fonts = Fonts.SystemFontFamilies.OrderBy(x => x.ToString()); 

    var latinFonts = fonts.Where(f => 
     f.Source.StartsWith("Global") || 
     (!IsSymbol(f) && HasLatinGlyphs(f))); 

    latinFonts.ToList().ForEach(Console.WriteLine); 
} 

private bool IsSymbol(FontFamily fontFamily) 
{ 
    GlyphTypeface glyph = GetFirstGlpyhTypeface(fontFamily); 

    return glyph.Symbol; 
} 

private bool HasLatinGlyphs(FontFamily fontFamily) 
{ 
    GlyphTypeface glyph = GetFirstGlpyhTypeface(fontFamily); 

    for (int i = 32; i < 127; i++) 
    { 
     if (!glyph.CharacterToGlyphMap.ContainsKey(i)) return false; 
    } 

    return true; 
} 

private GlyphTypeface GetFirstGlpyhTypeface(FontFamily fontFamily) 
{ 
    Typeface typeface = fontFamily.GetTypefaces().First(); 

    GlyphTypeface glyph; 

    typeface.TryGetGlyphTypeface(out glyph); 

    return glyph; 
} 

Aggiornamento 2

Si potrebbe sperimentare con filtrando i font in base a ciò che hanno il supporto per i caratteri latini estesi includendo filtri per Latin Extended-A e Latin Extended-B gamme. Il filtro con Latin Extended-A e Latin Extended-B lascia ancora pochi caratteri, ma il solo filtro su Latin Extended-A lascia ancora molti tipi di carattere. Rimuove automaticamente anche Mongolian Baiti in quanto questo ha solo il supporto per Latin-1 e Latin-1 Supplement.

Se questo tipo di analisi dà risultati desiderabili è altamente soggettivo. Qualcosa di sperimentare però:

private bool HasLatinGlyphs(FontFamily fontFamily) 
{ 
    GlyphTypeface glyph = GetFirstGlpyhTypeface(fontFamily); 

    List<Tuple<int, int>> ranges = new List<Tuple<int, int>> 
    { 
     new Tuple<int, int>(32, 126), //Latin-1 
     new Tuple<int, int>(160, 255), //Latin-1 Supplement 
     new Tuple<int, int>(256, 383), //Latin Extended-A 
     new Tuple<int, int>(384, 591), //Latin Extended-B 
    }; 

    foreach (Tuple<int, int> range in ranges) 
    { 
     for (int i = range.Item1; i <= range.Item2; i++) 
     { 
      if (!glyph.CharacterToGlyphMap.ContainsKey(i)) return false; 
     } 
    } 

    return true; 
} 

Anche in questo caso, altamente soggettivo, ma il seguente darà font che supportano glifi latini più un sub-set di caratteri internazionali di valuta:

List<Tuple<int, int>> ranges = new List<Tuple<int, int>> 
{ 
    new Tuple<int, int>(32, 126),  //Latin-1 
    new Tuple<int, int>(0x20A0, 0x20B5), //Currency Symbols (Partial) 
}; 

Update 3

Oltre alla tua domanda, modifica qui è una versione che funzionerà con Windows 7. Sfrutta la funzionalità di carattere nascosto di Windows 7 (come sottolineato da @Rick Sladkey) che per impostazione predefinita nasconde i caratteri che non sono cons pensato per essere utile per le impostazioni locali dell'utente corrente. Inoltre esclude i caratteri di simbolo:

[Test] 
public void test() 
{ 
    var allFonts = Fonts.SystemFontFamilies.OrderBy(x => x.Source); 

    var filteredFonts = allFonts.Where(f => 
     IsComposite(f) || (!IsSymbol(f) && !IsHidden(f))); 

    filteredFonts.ToList().ForEach(Console.WriteLine); 
} 

private static bool IsComposite(FontFamily fontFamily) 
{ 
    return fontFamily.Source.StartsWith("Global"); 
} 

private static bool IsSymbol(FontFamily fontFamily) 
{ 
    Typeface typeface = fontFamily.GetTypefaces().First(); 
    GlyphTypeface glyph; 
    typeface.TryGetGlyphTypeface(out glyph); 
    return glyph.Symbol; 
} 

private static bool IsHidden(FontFamily fontFamily) 
{ 
    const string Key = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Font Management"; 
    const string Value = "Inactive Fonts"; 
    RegistryKey key = Registry.CurrentUser.OpenSubKey(Key); 
    IEnumerable<string> hiddenFonts = (string[])key.GetValue(Value); 
    return hiddenFonts.Contains(fontFamily.Source); 
} 
+0

+1, risposta molto impressionante! –

+0

ottima risposta, grazie! –

1

Non ho trovato un modo facile affidabile per fare ciò. Penso che la soluzione migliore sia quella di onorare lo stato nascosto dei caratteri come previsto da Windows 7. In questo modo gli utenti possono nascondere/mostrare i caratteri come meglio credono, ma anche nascondere tutti i font inappropriati per la loro regione. Sfortunatamente non ci sono API documentate per scoprire se un font è nascosto o meno, ma puoi usare un'impostazione di registro.

Ecco un articolo che spiega come farlo:

Problemi correlati