J'ai implémenté une méthode de Justify assez rudimentaire pour dessiner une corde, mais je voudrais l'optimiser pour que l'espacement soit un peu plus dispersé.Justifier une chaîne manuellement pour la méthode DrawString() en C#
Ce que j'ai à ce jour, est la suivante:
string lastword = line.Split(' ').Last();
string lineNoLastWord = line.Substring(0,line.LastIndexOf(" ")).Trim();;
g.DrawString(lineNoLastWord, Font, brush, textBounds, sf);
g.DrawString(lastword, Font, brush, textBounds, ConvertAlignment(System.Windows.TextAlignment.Right));
ConvertAlignment
est une méthode personnalisée comme suit:
private StringFormat ConvertAlignment(System.Windows.TextAlignment align) {
StringFormat s = new StringFormat();
switch (align) {
case System.Windows.TextAlignment.Left:
case System.Windows.TextAlignment.Justify:
s.LineAlignment=StringAlignment.Near;
break;
case System.Windows.TextAlignment.Right:
s.LineAlignment=StringAlignment.Far;
break;
case System.Windows.TextAlignment.Center:
s.LineAlignment=StringAlignment.Center;
break;
}
s.Alignment = s.LineAlignment;
return s;
}
Le résultat est proche, mais a besoin d'un ajustement des espaces dans la chaîne lineNoLastWord
.
Un peu plus d'arrière-plan derrière le code. line
est le résultat d'une méthode qui est responsable de détecter si la chaîne sort des limites (largeur), et de la casser en lignes et mots, en se cassant et en mesurant pour s'assurer que la ligne complète reste dans la largeur de la zone être tiré sur. Le procédé met en oeuvre d'autres propriétés dans une ici classe, mais beaucoup plus grande est l'essentiel de celui-ci:
internal LineBreaker breakIntoLines(string s, int maxLineWidth) {
List<string> sResults = new List<string>();
int stringHeight;
int lineHeight;
int maxWidthPixels = maxLineWidth;
string[] lines = s.Split(new string[] { "\n", "\r\n" }, StringSplitOptions.None);
using (Graphics g=Graphics.FromImage(Pages[CurrentPage - 1])) {
g.CompositingQuality = CompositingQuality.HighQuality;
if (maxLineWidth<=0||maxLineWidth>(Pages[CurrentPage-1].Width-X)) {
maxWidthPixels=Pages[CurrentPage-1].Width-X;
}
lineHeight = (Int32)(g.MeasureString("X", Font).Height*(float)((float)LineSpacing/(float)100));
stringHeight = (Int32)g.MeasureString("X", Font).Height;
foreach (string line in lines) {
string[] words=line.Split(new string[] { " " }, StringSplitOptions.None);
sResults.Add("");
for (int i=0; i<words.Length; i++) {
if (sResults[sResults.Count-1].Length==0) {
sResults[sResults.Count-1]=words[i];
} else {
if (g.MeasureString(sResults[sResults.Count-1]+" "+words[i], Font).Width<maxWidthPixels) {
sResults[sResults.Count-1]+=" "+words[i];
} else {
sResults.Add(words[i]);
}
}
}
}
}
return new LineBreaker() {
LineHeight = lineHeight,
StringHeight = stringHeight,
MaxWidthPixels = maxWidthPixels,
Lines = sResults
};
}
internal class LineBreaker {
public List<string> Lines { get; set; }
public int MaxWidthPixels { get; set; }
public int StringHeight { get; set; }
public int LineHeight { get; set; }
public LineBreaker() {
Lines = new List<string>();
MaxWidthPixels = 0;
StringHeight = 0;
LineHeight = 0;
}
public LineBreaker(List<string> lines, int maxWidthPixels, int stringHeight, int lineHeight) {
Lines = lines;
MaxWidthPixels = maxWidthPixels;
LineHeight = lineHeight;
StringHeight = stringHeight;
}
}
L'image suivante illustre le problème que cela cause:
J'ai aussi vu this stackoverflow question and answers, et a trouvé que c'est aussi un moyen inefficace de l'espace en raison de la taille inconnue de la chaîne et la largeur inconnue du document se traduira par la chaîne trop longue si trop de mots, ou trop court avec rien de juste justifié. Une justification complète signifie que le texte s'aligne à la fois sur le côté gauche et sur le côté droit, et généralement, le contenu à l'intérieur est aussi espacé que possible. C'est comme ça que je voudrais l'implémenter.
La solution, est probablement un calcul sur les chaînes lastWord
et lineNoLastWord
avec des mesures pour assurer la viabilité de la production en ce que pas deux mots dans la chaîne se déroulera ou tassent, et il n'y aura pas de rembourrage sur la côté droit, mais à gauche peut encore contenir un retrait ou une languette. Une autre partie à considérer est que si la chaîne est plus courte qu'un certain seuil, aucune justification ne doit être appliquée.
MISE À JOUR
J'ai le concept suivant, qui devrait fonctionner, juste besoin d'obtenir le mot de l'index spécifié et insérez l'espace approprié:
int lastwordwidth = (Int32)g.MeasureString(" " + lastword, Font).Width;
int extraspace=lines.MaxWidthPixels-(Int32)(g.MeasureString(" "+lineNoLastWord, Font).Width+lastwordwidth);
int totalspacesneeded = (Int32)Math.Floor((decimal)(extraspace/lines.SpaceWidth));
int spacecount = lineNoLastWord.Count(x => x == ' ');
int currentwordspace = 0;
for (int i=0; i<spacecount; i++) {
if (currentwordspace>spacecount) { currentwordspace = 0; }
// insert spaces where spaces already exist between each word
// use currentwordspace to determine which word to replace with a word and another space
if (currentwordspace==0) {
// insert space after word
} else {
// insert space before word
}
currentwordspace++;
}
Avez-vous pas [ce] (http://csharphelper.com/blog/2014/10/fully-justify-a-line-of-text-in- c /)? Si vous calculez un flottant pour savoir jusqu'où se déplacer après __each__ mot, il répartit les mots __evenly__ sur la ligne .. – TaW
Oui, j'ai vu que je suppose que je pourrais insérer de manière sélective des espaces qui seraient plus rapides et donc moins d'appels graphiques. –
Eh bien vous pouvez ou mieux vous pouvez utiliser n-espaces mais vous aurez besoin de connaître leur largeur plutôt précisément, ce qui n'est pas aussi simple que cela puisse paraître, étant donné que la plupart des appels ne seront pas aussi précis que prévu. ne vous inquiétez pas des performances avant que vous n'ayez un problème. (Voir ['optimisation prématurée'] (http://c2.com/cgi/wiki?PrematureOptimization) ;-) – TaW