12
votes

Calcul de la largeur de texte informatique efficacement

Je dois calculer la largeur d'une colonne avec de nombreuses lignes (fonctionnalité d'autosize de colonne). En utilisant canvas.textwidth est beaucoup trop lent.

solution actuelle: Ma solution actuelle utilise une classe de mesureurs de texte qui crée une table de recherche pour un alphabet fixe une fois, puis calcule la largeur d'une chaîne donnée très rapidement en ajoutant des largeurs de caractères extraites de la Table de recherche. Pour les caractères non contenus dans la table de recherche, la largeur de caractères moyenne est utilisée (également calculée une fois).

problème: Cela fonctionne bien pour les langues européennes mais pas pour les langues asiatiques.

question: Quelle est la meilleure façon de faire face à ce problème? Comment une telle fonctionnalité d'autosize peut-elle être réalisée sans que les fonctions de canevas relativement lentes et sans dépendre d'un alphabet spécifique?

Merci pour une aide.


4 commentaires

Pourquoi ne pas prendre le caractère le plus large et se multiplier avec la plus longue taille de chaîne?


@Lu RD: Parce que c'est très inexact. Ceci était en fait ma première solution, mais l'erreur est immense.


Et une police de la taille fixe est également exclue?


Oui, la police est donnée et ne doit pas être modifiée à cause de difficultés techniques.


3 Réponses :


5
votes

J'ai bien peur que vous ayez à utiliser canvas.textwidth ou votre implémentation sera imprécise. La largeur du texte dépend du Font Kenting , où différentes séquences de caractères peuvent avoir différentes largeurs (non seulement le total des largeurs de caractères individuels).


5 commentaires

L'approche de la table de recherche a fonctionné correctement pour moi, donc il semble que, avec notre police et nos valeurs, la précision est bonne Engouh. Merci pour l'indice cependant! Je préfère définitivement la vitesse sur la précision ici.


Je ne suis pas sûr à 100% à ce sujet, mais je pense que certains scripts complexes contiennent des combinaisons de personnages adjacents dans différents glyphes, de sorte qu'une recherche basée sur des personnages en principe ne vous aidera pas, j'ai peur.


Votre commentaire "L'approche de la table de recherche a fonctionné correctement pour moi" contradictons votre déclaration: "Problème: cela fonctionne bien pour les langues européennes mais pas pour les langues asiatiques."


J'aurais dû dire "Travaillé d'accord pour moi ... jusqu'à ce que je découvre le problème des langues asiatiques" :) Ce que je voulais dire, c'est que la précision était tout à fait acceptable.


Effectivement pas faux. Voir Rudy et mes commentaires dans la réponse de MJ2008. J'ai édité et roulé de votre réponse afin que je puisse changer de -1 à +1.



3
votes

Moi, j'ai découpé le Middle-Man et utilisez directement l'API Windows. Spécifiquement, j'utilise GetTextextextPoint32 avec le .handle de la toile. Il n'y a rien que vous puissiez faire pour être plus rapide, autre que la mise en cache en quelque sorte, et franchement, vous ajouterez des frais généraux.


14 commentaires

Est-ce plus rapide que d'appeler canvas.textwidth ? Et plus vite, je veux dire considérablement plus vite ... pas seulement une fonction appeler moins ou quelque chose?


Pourquoi cela serait-il plus rapide que tcanvas.textwidth ? Toutes les frais généraux sont dans le code Win32. La couche de VCL mince n'est pas significative.


Cela n'a aucun sens, comme dit David.


Regardez le code! TextWidth appelle textextent qui vérifie que la toile est dans l'état requis. Vous avez coupé trois appels par chèque. Maintenant, cela ne va pas mettre le feu au monde, mais cela permet d'économiser plusieurs fois dans une boucle.


TIME IT. Mon argent dit que vous ne pourrez pas dire la différence.


@David, j'ai vraiment fait le temps. Mon code établit plus de 100 pages et cela a créé 100 000 objets, donc j'ai fait beaucoup de timing.


Et quels sont les résultats de l'expérience de synchronisation comparant gettextextendpoint32 et tcanvas.textwidth ?


Deuxièmement que. Si la vitesse est une question ultime et n est grande, la microoptimisation en utilisant des appels GDI directes est appropriée. BTW, @ mj2008 à l'aide de toile.Handle peut être sous-optimal également (invoque Getter et Handleneed / Handleallocated)


@Downvoter Oui, vous avez raison et, dans mon code, je cache la poignée aussi.


@David, c'est quelques années maintenant depuis que j'ai fait le timing, donc je n'ai pas les résultats. Mais sachez que vous pouvez gagner du temps lorsque vous appelez cela beaucoup, même si ce n'est pas beau, vaut la peine de savoir.


@Downvoter handledned / HandleDallOcated est des noops comparés à gettextextentpoint32 .


GettextextentCoint32 est le seul moyen correct d'obtenir des largeurs exactes avec le texte Unicode. C'est la manière dont Windows détermine comment plier correctement les lignes et Microsoft l'optimisez-la à la poignée. +1 Voir: Stackoverflow.com/questions/3497471/wysiwig-voit -unicode / ...


@lkessler: Tu m'as dit que "ça compte". Le calendrier peut importer si vous devez vérifier de nombreux éléments. Pour quelques-uns (comme 4 ou 5 par colonne), il ne le fera pas. Et gettextextTentCoint32 est ce qu'est ce qu'on utilise Canvas.TextWidth, il s'agit donc d'une précision équivalente de WRT.


@Rudy: Désolé. Vous avez raison. N'a pas réalisé les appels de TextWidth GetTextextextPoint32. Parce que vous avez édité votre réponse, cela m'a permis de supprimer le -1 et je vous ai donné un +1 à la place.



9
votes

Vous avez dit que vous souhaitez obtenir la largeur maximale du texte pour une colonne. Ne pouvez-vous pas dire que les 4 ou 5 cordes les plus longues et obtiennent leurs largeurs? De cette façon, vous n'aurez pas à trouver la largeur de tous les articles et peut économiser un certain temps.

ou vous utilisez votre cache pour trouver la longueur rugueuse des chaînes, puis affiner cela en obtenant la largeur réelle des 4 ou 5 éléments du top 4 ou 5.

Je ne pense pas que cela compte beaucoup si vous utilisez Canvas.TextWidth ou GetTextextextPoint32. Il suffit d'utiliser l'un d'entre eux pour obtenir les largeurs exactes, après avoir utilisé l'une des méthodes ci-dessus pour estimer les chaînes les plus longues / les plus larges.

à ceux qui pensent que cela ne fonctionne pas

Si l'affiche de la question initiale pense que cela pourrait fonctionner, je n'ai aucune raison de penser que ce ne sera pas. Il sait mieux quel genre de chaînes peut être dans les colonnes qu'il a.

Mais ce n'est pas mon argument principal. Il a déjà écrit qu'il fait une filtration préliminaire en ajoutant les largeurs individuelles prédéterminées des personnages. Cela ne prend en compte aucun crénage. Eh bien, la kernage ne peut faire qu'une chaîne plus étroite , il est donc logique de ne vérifier que les 4 ou 5 éléments de haut niveau pour la largeur exacte. Le plus gros problème qui peut survenir est que la colonne pourrait être de quelques pixels trop larges, pas plus. Mais ce sera beaucoup plus rapide que d'utiliser TextWidth ou GetTextextExtPoint32 ou des fonctions similaires sur chaque entrée (en supposant plus de 5 entrées), et c'est ce que l'affiche originale souhaitée. Je suggère que ceux qui ne me croyaient pas simplement l'essayer.

Quant à l'utilisation de la longueur de la chaîne pure: même cela est probablement assez bon. Oui, 'www' est probablement plus large que "!!!!!", mais l'affiche originale saura probablement le meilleur type de matériel de chaîne qu'il a, et s'il est réalisable. '!!!!!' ou 'www' ne sont pas les entrées habituelles que l'on s'attend. Surtout si vous considérez que non seulement une seule chaîne est vérifiée, mais que les 4 ou 5 chaînes les plus longues (ou quel que soit le numéro qui s'avère optimal). Il est très peu probable que la chaîne la plus large ne soit pas parmi elles. Mais l'affiche originale peut indiquer si cela est possible ou réalisable. Il semble penser que c'est.

Alors arrêtez la descente et essayez-le pour vous-même.


5 commentaires

C'est très pauvre. Est "!!!!!" plus long ou plus court que "www" dans votre police? Vous ne pouvez pas utiliser le nombre de caractères à couper rapidement la largeur du texte, en particulier avec la fusion et les ajustements de sous-pixels effectués par Windows.


@ MJ2008: Il pourrait y avoir des cas extrêmes où cela ne fonctionne pas, mais je pense que pour des valeurs de mots réels, cela devrait fonctionner assez bien. Notez que la suggestion n'est pas de mesurer uniquement la chaîne la plus longue, mais de mesurer les cordes les plus longues 4-5. Les chances sont plutôt bonnes que vous attrapez la chaîne la plus longue "métriquement".


@ MJ2008: C'est pourquoi j'ai dit qu'il devrait prendre le top 4 ou 5, pas seulement le "plus long". Et si sa méthode a toutes les largeurs de caractère indivitaires, cache de toute façon, il a déjà une estimation approximative. Cela a juste besoin d'un raffinement.


@lkessler: Voir ma mise à jour. Je suis sûr que cela fonctionnera, ou du moins bien fonctionnera suffisamment pour ce que veut l'affiche originale. Mais ce n'est pas tout à fait sûr de ce que vous voulez dire avec "ça compte". Que fait? La méthode pour obtenir la largeur de texte? Je doute que cela compte, pour seulement 4 ou 5 entrées. Apparemment, l'affiche originale a de nombreuses personnes de plus de 5, sinon la vitesse ne serait pas un problème.


@Rudy: Bon pour moi maintenant. +1 Voir mon commentaire sous votre commentaire dans la réponse de MJ2008.