2010-12-27 17 views
9

Estoy seguro de que me falta algo obvio, tengo un área en la que tengo la intención de dibujar texto. Sé su (el área) de alto y ancho. Deseo saber cuántos caracteres/Palabras encajarán en el ancho, preferiblemente caracteres. Segunda pregunta, si la línea es demasiado larga, querré dibujar una segunda línea, así que supongo que también necesito obtener el alto del texto, incluido lo que considere el acolchado vertical correcto.Longitud de la cadena que cabe en un ancho específico

También me gustaría saber la inversa, es decir, cuántos caracteres puedo caber en un ancho específico.

Supongo que el hecho de que WPF no esté limitado a píxeles tendrá alguna relación con la respuesta.

En última instancia, estoy planeando envolver el texto con imágenes de formas irregulares incrustadas en el texto.

Cualquier apunta en la dirección correcta sería genial.

Gracias

Respuesta

15

Para WPF puede usar la clase FormattedText para calcular cuánto ancho usará una cadena de texto determinada, esto dependerá del texto real.

Ejemplo:

FormattedText formattedText = new FormattedText("Hello Stackoverflow", 
               System.Globalization.CultureInfo.GetCultureInfo("en-us"), 
               FlowDirection.LeftToRight, 
               new Typeface("Arial"), FontSize =14, Brushes.Black); 
double textWidth = formattedText.Width; 

Obtención de una cadena secundaria para una anchura dada (simplificado):

string text = GetSubStringForWidth("Hello Stackoverflow", 55); 
... 
public string GetSubStringForWidth(string text, double width) 
{ 
    if (width <= 0) 
     return ""; 

    int length = text.Length; 
    string testString; 

    while(true)//0 length string will always fit 
    { 
     testString = text.Substring(0, length); 
     FormattedText formattedText = new FormattedText(testString, 
                 CultureInfo.GetCultureInfo("en-us"), 
                 FlowDirection.LeftToRight, 
                 new Typeface("Arial"), 
                 FontSize = 14, 
                 Brushes.Black); 
     if(formattedText.Width <= width) 
      break; 
     else 
      length--; 
    } 
    return testString; 
} 
+0

Excelente comienzo, ¿tenemos alguna idea de cómo hacer todo lo contrario o necesito aplicar algún tipo de división? y conquistar la estrategia? es decir, mida "¿Soy demasiado largo para caber en este espacio?" y luego, si> ancho disponible, mida "¿Soy demasiado largo?" y si es demasiado corto, intente algo entre medio? – Ian

+0

@Ian: debería ser sencillo escribir un método que devuelva el número de caracteres que se ajusta a un ancho determinado, comenzaría con algo así como mi edición anterior (obviamente simplificada), y si el rendimiento es un problema, vaya a algo más complicado - No he hecho ninguna medición. – BrokenGlass

+0

Sí. ¿Cuán exacto es cada char de una cuerda desconocida? Imposible, solo puede ser "asumido" (promediado). – TomTom

-1

esto es imposible porque los personajes tienen diferente longitud, por ejemplo W es mucho más amplio de lo que (a menos que esté utilizando un tipo de letra como Courier New).

+1

"Un aficionado como Courier New" - http://en.wikipedia.org/wiki/Monospaced_font –

+0

no es imposible. Toda la tipografía depende de poder hacer esto. Históricamente hemos tenido cosas como MeasureText y MeasureString, y el evento Measure en los elementos. Piénselo, observe la página web que está leyendo, observe cómo encaja el texto en el espacio disponible. – Ian

+0

Ah, 'pero la tipografía no funciona como tú. Esto es como construir una casa desde el techo. No sabe cuántos caracteres caben en un ancho, usted sabe qué tan ancho es un TEXTO ESPECÍFICO. Sin conocer el texto, no es posible calcular el ancho. Typogrpahy no comienza con un "texto aleatorio desconocido" para ser medido. – TomTom

3

@ respuesta de BrokenGlass es grande, pero dependiendo de las características de su aplicación , puede encontrar que obtiene un mejor rendimiento con una búsqueda binaria. Si la mayoría de sus cadenas encajan en el ancho disponible, o generalmente solo necesitan ser recortadas por un carácter o dos, entonces una búsqueda lineal es lo mejor. Sin embargo, si tiene muchas cadenas largas que se truncarán severamente, la siguiente búsqueda binaria funcionará bien.

Tenga en cuenta que tanto availableWidth como fontSize se especifican en unidades independientes del dispositivo (1/96 de pulgada). Además, use el TextFormattingMode que coincida con la forma en que dibuja su texto.

public static string TruncateTextToFitAvailableWidth(
    string text, 
    double availableWidth, 
    string fontName, 
    double fontSize) 
{ 
    if(availableWidth <= 0) 
     return string.Empty; 

    Typeface typeface = new Typeface(fontName); 

    int foundCharIndex = BinarySearch(
     text.Length, 
     availableWidth, 
     predicate: (idxValue1, value2) => 
     { 
      FormattedText ft = new FormattedText(
       text.Substring(0, idxValue1 + 1), 
       CultureInfo.CurrentCulture, 
       FlowDirection.LeftToRight, 
       typeface, 
       fontSize, 
       Brushes.Black, 
       numberSubstitution: null, 
       textFormattingMode: TextFormattingMode.Ideal); 

      return ft.WidthIncludingTrailingWhitespace.CompareTo(value2); 
     }); 

    int numChars = (foundCharIndex < 0) ? ~foundCharIndex : foundCharIndex + 1; 

    return text.Substring(0, numChars); 
} 

/** 
<summary> 
See <see cref="T:System.Array.BinarySearch"/>. This implementation is exactly the same, 
except that it is not bound to any specific type of collection. The behavior of the 
supplied predicate should match that of the T.Compare method (for example, 
<see cref="T:System.String.Compare"/>). 
</summary> 
*/  
public static int BinarySearch<T>(
    int    length, 
    T     value, 
    Func<int, T, int> predicate) // idxValue1, value2, compareResult 
{ 
    return BinarySearch(0, length, value, predicate); 
} 

public static int BinarySearch<T>(
    int    index, 
    int    length, 
    T     value, 
    Func<int, T, int> predicate) 
{ 
    int lo = index; 
    int hi = (index + length) - 1; 

    while(lo <= hi) 
    { 
     int mid = lo + ((hi - lo)/2); 

     int compareResult = predicate(mid, value); 

     if(compareResult == 0) 
      return mid; 
     else if(compareResult < 0) 
      lo = mid + 1; 
     else 
      hi = mid - 1; 
    } 

    return ~lo; 
} 
+0

su respuesta es demasiado complicada, hay una solución mucho más simple. todo lo que necesita hacer es calcular el porcentaje de cuánto es demasiado pequeño o demasiado grande para el espacio dado y escalar el tamaño de la fuente con ese factor. – henon

+0

Cambiar el tamaño de fuente no es aceptable ni para el OP ni para nuestra aplicación. –

Cuestiones relacionadas