/// <summary> /// Returns Image with <paramref name="str"/> drawn wrapped to given <paramref name="width"/>. /// </summary> /// <param name="str"><see cref="string"/> to be drawn.</param> /// <param name="width">Width that string will be wrapped to.</param> /// <param name="fmtCharacter"><see cref="CharacterFormat"/> used to draw the <see cref="string"/>. </param> /// <param name="fmtParagraph"><see cref="ParagraphFormat"/> used to draw the <see cref="string"/>. </param> /// <returns><see cref="Image"/> with given <paramref name="str"/> drawn.</returns> public Image DrawWrappedString(string str, float width, CharacterFormat fmtCharacter, ParagraphFormat fmtParagraph) { float height = GetWrappedHeight(DummyGraphics, str, fmtCharacter, width); ParagraphFormat pf2 = fmtParagraph.ShallowCopy(); pf2.MultiLine = true; pf2.ShowIncompleteLines = true; pf2.VerticalAlignment = ParagraphVerticalAlignment.Top; return DrawStringInRectangle(str, new SizeF(width, height), fmtCharacter, fmtParagraph); }
/// <summary> /// Draws string with given character format at given <paramref name="pnt"/>. /// </summary> /// <param name="img"><see cref="Image"/> object to draw into.</param> /// <param name="str"><see cref="string"/> to be drawn.</param> /// <param name="pnt">Top-left <see cref="Point"/> of the drawn string.</param> /// <param name="fmt"><see cref="CharacterFormat"/> used to draw the <see cref="string"/>. </param> /// <remarks>There's a problem while drawing strings using ClearType on transparent /// background. Resulting text is going to look thick and jagged. If this applies to you, use /// rather Image DrawString(string, CharacterFormat) or /// Image DrawStringInRectangle(string, SizeF, CharacterFormat, ParagraphFormat) where the /// problem is corrected (at cost of a slight performance loss).</remarks> public void DrawString(Image img, string str, PointF pnt, CharacterFormat fmt) { Graphics gr = Graphics.FromImage(img); DrawString(gr, str, pnt, fmt); gr.Dispose(); }
/// <summary> /// Returns <see cref="Image"/> object with <paramref name="str"/> drawn. /// </summary> /// <param name="str"><see cref="string"/> to be drawn.</param> /// <param name="fmt"><see cref="CharacterFormat"/> used to draw the <paramref name="str"/>. </param> /// <returns><see cref="Image"/> with given <paramref name="str"/> drawn. The image is sized /// just to fit the string.</returns> public Image DrawString(string str, CharacterFormat fmt) { /*Size size=Size.Ceiling(dummyGraphics.MeasureString(str, fmt.Font)); Image res=new Bitmap(size.Width, size.Height); DrawString(res, str, new Point(0,0), fmt); return res;*/ ParagraphFormat pf = new ParagraphFormat(ParagraphAlignment.Left, ParagraphVerticalAlignment.Top, false, true, StringTrimming.None, null); SizeF sz = MeasureStringExactly(dummyGraphics, str, fmt, new ParagraphFormat(), true); sz.Width += 2*GetMeasureStringVerticalGap(dummyGraphics, fmt.Font); sz.Height += 2*GetMeasureStringHorizontalGap(dummyGraphics, fmt.Font); if ((fmt.Font.Style & FontStyle.Italic) != 0) sz.Width += 4; else sz.Width += 2; return DrawStringInRectangle(str, sz, fmt, pf); }
/// <summary> /// Draws string with given formating and width at specified point. /// </summary> /// <param name="img"><see cref="Image"/> object to draw into.</param> /// <param name="str"><see cref="string"/> to be drawn.</param> /// <param name="width">Width that string will be wrapped to.</param> /// <param name="ofs">Top-left point of the drawn string.</param> /// <param name="fmtCharacter"><see cref="CharacterFormat"/> used to draw the <see cref="string"/>. </param> /// <param name="fmtParagraph"><see cref="ParagraphFormat"/> used to draw the <see cref="string"/>. </param> public void DrawWrappedString(Image img, string str, PointF ofs, float width, CharacterFormat fmtCharacter, ParagraphFormat fmtParagraph) { Graphics gr = Graphics.FromImage(img); DrawWrappedString(gr, str, ofs, width, fmtCharacter, fmtParagraph); gr.Dispose(); }
/// <summary> /// Draws string with given formating into given rectangle. /// </summary> /// <param name="img"><see cref="Image"/> object to draw into.</param> /// <param name="str"><see cref="string"/> to be drawn.</param> /// <param name="rect"><see cref="Rectangle"/> that string is drawn into.</param> /// <param name="fmtCharacter"><see cref="CharacterFormat"/> used to draw the <paramref name="str"/>. </param> /// <param name="fmtParagraph"><see cref="ParagraphFormat"/> used to draw the <paramref name="str"/>. </param> /// <remarks>There's a problem while drawing strings using ClearType on transparent /// background. Resulting text is going to look thick and jagged. If this applies to you, use /// rather Image DrawString(string, CharacterFormat) or /// Image DrawStringInRectangle(string, SizeF, CharacterFormat, ParagraphFormat) where the /// problem is corrected (at cost of a slight performance loss).</remarks> public void DrawStringInRectangle(Image img, string str, RectangleF rect, CharacterFormat fmtCharacter, ParagraphFormat fmtParagraph) { Graphics gr = Graphics.FromImage(img); DrawStringInRectangle(gr, str, rect, fmtCharacter, fmtParagraph); gr.Dispose(); }
/// <summary> /// Adds string with given format to the end of this formatted string. /// </summary> /// <param name="str"></param> /// <param name="fmt"></param> private void AddFormattedPiece(string str, CharacterFormat fmt) { //No reason for storing empty pieces (and lots against it) if(str.Length==0) return; //Rotation is matter of global format, piece formats doesn't implement //it (so it angle to be zero) Debug.Assert(fmt.Angle==0); m_strings.Add(str); m_formats.Add(fmt); }
/// <summary> /// Returns <see cref="StringFormat"/> object that fits best to given <see cref="CharacterFormat"/> /// and <see cref="ParagraphFormat"/> objects. /// </summary> /// <param name="fmtCharacter"><see cref="CharacterFormat"/> object</param> /// <param name="fmtParagraph"><see cref="ParagraphFormat"/> object</param> /// <returns><see cref="StringFormat"/> object</returns> internal StringFormat GetStringFormat(CharacterFormat fmtCharacter, ParagraphFormat fmtParagraph) { StringFormat sf = (StringFormat) StringFormat.GenericDefault.Clone(); sf.Alignment = ParAl2StrAl(fmtParagraph.Alignment); sf.LineAlignment = ParVertAl2StrAl(fmtParagraph.VerticalAlignment); sf.HotkeyPrefix = fmtCharacter.HotkeyPrefix; sf.Trimming = fmtParagraph.Trimming; if (!fmtParagraph.ShowIncompleteLines) sf.FormatFlags |= StringFormatFlags.LineLimit; if (!fmtParagraph.MultiLine) sf.FormatFlags |= StringFormatFlags.NoWrap; return sf; }
/// <summary> /// Draws string with given formating and width at specified point. /// </summary> /// <param name="gr"><see cref="Graphics"/> object to draw into.</param> /// <param name="str"><see cref="string"/> to be drawn.</param> /// <param name="width">Width that string will be wrapped to.</param> /// <param name="ofs">Top-left point of the drawn string.</param> /// <param name="fmtCharacter"><see cref="CharacterFormat"/> used to draw the <see cref="string"/>. </param> /// <param name="fmtParagraph"><see cref="ParagraphFormat"/> used to draw the <see cref="string"/>. </param> public void DrawWrappedString(Graphics gr, string str, PointF ofs, float width, CharacterFormat fmtCharacter, ParagraphFormat fmtParagraph) { float height=GetWrappedHeight(gr, str, fmtCharacter, width); ParagraphFormat pf2=fmtParagraph.ShallowCopy(); pf2.MultiLine=true; pf2.ShowIncompleteLines=true; pf2.VerticalAlignment=ParagraphVerticalAlignment.Top; DrawStringInRectangle(gr, str, new RectangleF(ofs, new SizeF(width, height)),fmtCharacter, fmtParagraph); }
/// <summary> /// Draws string with given formating into given <paramref name="rectangle"/>. /// </summary> /// <param name="gr"><see cref="Graphics"/> object to draw into.</param> /// <param name="str"><see cref="string"/> to be drawn.</param> /// <param name="rectangle"><see cref="Rectangle"/> that string is drawn into.</param> /// <param name="fmtCharacter"><see cref="CharacterFormat"/> used to draw the <see cref="string"/>. </param> /// <param name="fmtParagraph"><see cref="ParagraphFormat"/> used to draw the <see cref="string"/>. </param> public void DrawStringInRectangle(Graphics gr, string str, RectangleF rectangle, CharacterFormat fmtCharacter, ParagraphFormat fmtParagraph) { Matrix trOld = gr.Transform.Clone(); gr.TranslateTransform(rectangle.X, rectangle.Y); gr.RotateTransform(fmtCharacter.Angle); gr.TranslateTransform(-rectangle.X, -rectangle.Y); if (fmtParagraph.BackgroundBrush != null) gr.FillRectangle(fmtParagraph.BackgroundBrush, rectangle); CharacterFormat cf = fmtCharacter.ShallowCopy(); cf.Angle = 0; ParagraphFormat pf = fmtParagraph.ShallowCopy(); pf.BackgroundBrush = null; if (fmtCharacter.Formatted) { SDUFormattedString fmts = new SDUFormattedString(str, cf); if (pf.MultiLine) fmts.WrapLines(gr, rectangle.Width); fmts.DrawStringInRectangle(gr, rectangle, pf); } else if (fmtCharacter.FilledBounds) gr.FillRectangle(fmtCharacter.Brush, rectangle); else { if (fmtParagraph.Alignment == ParagraphAlignment.Full && fmtParagraph.MultiLine && rectangle.Width > 0) { DrawFullAlignedStringInRectangle(gr, str, rectangle, cf, fmtParagraph); } else { StringFormat sf = GetStringFormat(fmtCharacter, fmtParagraph); gr.DrawString(str, fmtCharacter.Font, fmtCharacter.Brush, rectangle, sf); } } gr.Transform = trOld; }
/// <summary> /// Returns height of the text when wrapping to given <paramref name="width"/> is performed. /// </summary> /// <param name="gr">Graphics object used for measurement.</param> /// <param name="str">String to be measured.</param> /// <param name="cf"><see cref="CharacterFormat"/> of given string.</param> /// <param name="width">Maximum width of resulting text.</param> /// <returns>Returns height of the text when wrapping to given <paramref name="width"/> /// is performed.</returns> public float GetWrappedHeight(Graphics gr, string str, CharacterFormat cf, float width) { CharacterFormat cf2=cf.ShallowCopy(); cf2.Angle=0; SDUFormattedString fmts = new SDUFormattedString(str, cf); fmts.WrapLines(gr, width); return fmts.GetTotalHeight(gr); }
/// <summary> /// Returns (more or less) exact size of given string. /// </summary> /// <param name="gr">Graphics where the size should be measured.</param> /// <param name="str">String to be measured.</param> /// <param name="cf">CharacterFormat of the string.</param> /// <param name="pf">ParagraphFormat of the string.</param> /// <param name="includeTrailingSpaces">Specifies whether trailing /// spaces should be included into measurement.</param> /// <returns>Returns size of given text at specified conditions.</returns> /// <remarks> /// Returned value is not affected by any gaps and inaccurancies which appear /// while using Graphics.MeasureString as we use Graphics.MeasureCharacterRanges. /// </remarks> public SizeF MeasureStringExactly(Graphics gr, string str, CharacterFormat cf, ParagraphFormat pf, bool includeTrailingSpaces) { StringFormat sf = GetStringFormat(cf, pf); if (includeTrailingSpaces) sf.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces; sf.SetMeasurableCharacterRanges(new CharacterRange[] {new CharacterRange(0, str.Length)}); sf.FormatFlags |= StringFormatFlags.NoWrap; SizeF bound = gr.MeasureString(str, (cf).Font, 0, sf); bound.Width += 10; bound.Height += 10; RectangleF rect = new RectangleF(new PointF(0, 0), bound); //RectangleF rect=new RectangleF(0,0,0,0); bool useOriginalGraphics = gr.DpiX != dummyGraphics.DpiX || gr.DpiY != dummyGraphics.DpiY || gr.PageScale != dummyGraphics.PageScale || gr.PageUnit != dummyGraphics.PageUnit; Region[] rgn; if (useOriginalGraphics) rgn = gr.MeasureCharacterRanges(str, cf.Font, rect, sf); else rgn = dummyGraphics.MeasureCharacterRanges(str, cf.Font, rect, sf); sf.Dispose(); SizeF size; if (useOriginalGraphics) size = rgn[0].GetBounds(gr).Size; else size = rgn[0].GetBounds(dummyGraphics).Size; rgn[0].Dispose(); return size; }
/// <summary> /// Draws full aligned string into given rectangle. /// </summary> /// <param name="gr"><see cref="Graphics"/> object to draw into.</param> /// <param name="str"><see cref="string"/> to be drawn.</param> /// <param name="rect"><see cref="Rectangle"/> that string is drawn into.</param> /// <param name="cf"><see cref="CharacterFormat"/> used to draw the <paramref name="str"/>. </param> /// <param name="pf"><see cref="ParagraphFormat"/> used to draw the <paramref name="str"/>. </param> /// <remarks>As this method isn't to be exposed as public, there are /// several limitations:<br/> /// <see cref="Rectangle.Width"/> has to be non-zero.<br/> /// <see cref="CharacterFormat.Angle"/> has to be zero.<br/> /// <see cref="ParagraphFormat.MultiLine"/> has to be false.<br/> /// <see cref="ParagraphFormat.Alignment"/> has to be <see cref="ParagraphAlignment.Full"/>.<br/> /// <see cref="ParagraphFormat.BackgroundBrush"/> has to be null.<br/> /// If not satisfied, results might be not as expected.</remarks> private void DrawFullAlignedStringInRectangle(Graphics gr, string str, RectangleF rect, CharacterFormat cf, ParagraphFormat pf) { Region oldClip = gr.Clip.Clone(); gr.IntersectClip(rect); float yofs = rect.Y; StringFormat sfLeft; //StringFormat for drawing left-aligned lines { ParagraphFormat pf2 = pf.ShallowCopy(); pf2.Alignment = ParagraphAlignment.Left; pf2.VerticalAlignment = ParagraphVerticalAlignment.Top; pf2.MultiLine = false; pf2.ShowIncompleteLines = true; sfLeft = GetStringFormat(cf, pf2); } //lineHeight & borders retrieving float lineHeight = GetLineHeight(gr, cf.Font); int lineCount; //lineCount retrieving { StringFormat sf = GetStringFormat(cf, pf); int charfit; gr.MeasureString(str, cf.Font, new SizeF(rect.Width, rect.Height), sf, out charfit, out lineCount); } //VerticalAlignment business switch (pf.VerticalAlignment) { case ParagraphVerticalAlignment.Bottom: yofs += rect.Height - lineHeight*lineCount; break; case ParagraphVerticalAlignment.Center: yofs += (rect.Height - lineHeight*lineCount)/2; break; } int flc = 0; //first line character int llc = flc; //last line character (actually the one after last) int lineIndex = 0; while (lineIndex < lineCount - 1 && flc < str.Length) { lineIndex++; llc = flc; //add spaces at the beginneing while (llc < str.Length && Char.IsWhiteSpace(str, llc)) llc++; //add words until the line is full int pllc = llc; //potential last line character while (llc == pllc && llc < str.Length) { pllc++; while (pllc < str.Length && !Char.IsWhiteSpace(str, pllc)) pllc++; if (gr.MeasureString(str.Substring(flc, pllc - flc), cf.Font).Width < rect.Width) llc = pllc; } //If nothing was added before, add single characters (rect.Width is too small) if (llc == flc) do { //always at least one character per line llc++; } while (llc < str.Length && gr.MeasureString(str.Substring(flc, llc - flc + 1), cf.Font).Width < rect.Width); string line = str.Substring(flc, llc - flc); //Newline fotmatting int nlindex = line.IndexOf('\n'); if (nlindex >= 0) { //Newline may be just "\n" or "\r\n" if (nlindex > 1 && line[nlindex - 1] == '\r') line = line.Substring(0, nlindex - 1); else line = line.Substring(0, nlindex); DrawString(gr, line, new PointF(rect.X, yofs), cf); flc += nlindex + 1; } else { DrawFullAlignedLine(gr, line, new PointF(rect.X, yofs), rect.Width, cf); flc = llc; //skip spaces at the end of line (just like Graphics.DrawString does) while (flc < str.Length && Char.IsWhiteSpace(str, flc)) flc++; } yofs += lineHeight; } //And here comes the last line - it's always drawn left-aligned //(also, we have to get rid of newlines or they will sometimes show //in the bottom) gr.DrawString(str.Substring(flc), cf.Font, cf.Brush, new RectangleF(rect.X, yofs, rect.Width, lineHeight), sfLeft); //Set back the original clipping region gr.Clip = oldClip; }
/// <summary> /// Draws one full aligned line of text (ie. spaces are expanded to fill /// exactly the given <paramref name="width"/>. /// </summary> /// <param name="gr"><see cref="Graphics"/> object to draw into.</param> /// <param name="line"><see cref="string"/> that is to be drawn.</param> /// <param name="pnt">Top-left <see cref="Point"/> of the line.</param> /// <param name="width">Width of the line.</param> /// <param name="cf"><see cref="CharacterFormat"/> used to draw the line. <br/> /// </param> /// <remarks>As this method isn't to be exposed as public, there are /// several limitations:<br/> /// <see cref="CharacterFormat.Angle"/> has to be zero.<br/> /// Given string has to fit into given width. <br/> /// If not satisfied, results might be not as expected.</remarks> private void DrawFullAlignedLine(Graphics gr, string line, PointF pnt, float width, CharacterFormat cf) { if (line.Length == 0) return; int whtcnt = 0; //count of white spaces for (int i = 0; i < line.Length; i++) if (Char.IsWhiteSpace(line, i)) whtcnt++; if (whtcnt == 0) DrawString(gr, line, pnt, cf); else { float lnowhtwidth = 0; //width of line without white spaces int fwc = 0; //first word character for (int i = 0; i < line.Length; i++) { if (Char.IsWhiteSpace(line, i)) { if (i > 0 && !Char.IsWhiteSpace(line, i - 1)) lnowhtwidth += gr.MeasureString(line.Substring(fwc, i - fwc), cf.Font).Width; } else if (i > 0 && Char.IsWhiteSpace(line, i - 1)) fwc = i; } if (!Char.IsWhiteSpace(line, line.Length - 1)) lnowhtwidth += gr.MeasureString(line.Substring(fwc), cf.Font).Width; float whtxofs = (width - lnowhtwidth)/whtcnt; float xofs = pnt.X; fwc = 0; for (int i = 0; i < line.Length; i++) { if (Char.IsWhiteSpace(line, i)) { if (i > 0 && !Char.IsWhiteSpace(line, i - 1)) { string word = line.Substring(fwc, i - fwc); DrawString(gr, word, new PointF(xofs, pnt.Y), cf); xofs += gr.MeasureString(word, cf.Font).Width; } xofs += whtxofs; } else if (i > 0 && Char.IsWhiteSpace(line, i - 1)) fwc = i; } if (!Char.IsWhiteSpace(line, line.Length - 1)) DrawString(gr, line.Substring(fwc), new PointF(xofs, pnt.Y), cf); } }
/// <summary> /// Returns an image with given string drawn. As in this method is not present the /// ClearType bug correction, it's only to be used with solid background brush.<br/> /// Merit of this method is that it can properly handle rotated text. /// </summary> private Image DrawSolidBackgroundStringInRectangle(string str, SizeF size, CharacterFormat fmtCharacter, ParagraphFormat fmtParagraph, out PointF topLeftOffset) { float angle = fmtCharacter.Angle%360; float nAngle = (float) ((angle%90)*Math.PI/180); //normalized angle in RADIANS! PointF nOfs; SizeF nSize; if (nAngle == 0 || fmtCharacter.Angle == 0) { nOfs = new PointF(0, 0); nSize = size; } else { nOfs = new PointF((float) Math.Sin(nAngle)*size.Height, 0); nSize = new SizeF( (float) (Math.Cos(nAngle)*size.Width + Math.Sin(nAngle)*size.Height), (float) (Math.Sin(nAngle)*size.Width + Math.Cos(nAngle)*size.Height)); } PointF rOfs; //Rotated top-left point SizeF rSize; //Rotated size if (angle < 90f) { rSize = nSize; rOfs = nOfs; } else if (angle < 180f) { rSize = new SizeF(nSize.Height, nSize.Width); //yes, width=height and height=width rOfs = new PointF(rSize.Width, nOfs.X); } else if (angle < 270f) { rSize = nSize; rOfs = new PointF(rSize.Width - nOfs.X, rSize.Height); } else { rSize = new SizeF(nSize.Height, nSize.Width); //yes, width=height and height=width rOfs = new PointF(0, rSize.Height - nOfs.X); } topLeftOffset = rOfs; Bitmap res = new Bitmap(Size.Ceiling(rSize).Width, Size.Ceiling(rSize).Height); Graphics gr = Graphics.FromImage(res); DrawStringInRectangle(gr, str, new RectangleF(rOfs, size), fmtCharacter, fmtParagraph); gr.Dispose(); return res; }
/// <summary> /// Returns Image with <paramref name="str"/> drawn into rectangle of given size. /// </summary> /// <param name="str"><see cref="string"/> to be drawn.</param> /// <param name="size">Size of rectangle string will be drawn into and also of the /// returned image.</param> /// <param name="fmtCharacter"><see cref="CharacterFormat"/> used to draw the <paramref name="str"/>. </param> /// <param name="fmtParagraph"><see cref="ParagraphFormat"/> used to draw the <paramref name="str"/>. </param> /// <param name="topLeftOffset">Out parameter which receives top-left point of the rotated string. </param> /// <returns><see cref="Image"/> with given <paramref name="str"/> drawn.</returns> public Image DrawStringInRectangle(string str, SizeF size, CharacterFormat fmtCharacter, ParagraphFormat fmtParagraph, out PointF topLeftOffset) { //Prepare mask CharacterFormat mcf = fmtCharacter.ShallowCopy(); ParagraphFormat mpf = fmtParagraph.ShallowCopy(); mcf.Brush = new SolidBrush(Color.Black); mpf.BackgroundBrush = new SolidBrush(Color.White); mcf.IgnoreColorFormatting = true; Bitmap bMask = (Bitmap) DrawSolidBackgroundStringInRectangle(str, size, mcf, mpf, out topLeftOffset); //Prepare foreground CharacterFormat fgcf = fmtCharacter.ShallowCopy(); ParagraphFormat fgpf = fmtParagraph.ShallowCopy(); fgcf.FilledBounds = true; fgpf.BackgroundBrush = null; Bitmap bFg = (Bitmap) DrawSolidBackgroundStringInRectangle(str, size, fgcf, fgpf, out topLeftOffset); //And blend it together CopyIntensityAsAlpha(bMask, bFg); bMask.Dispose(); //Using background if (fmtParagraph.BackgroundBrush != null) { Bitmap bRes = new Bitmap(bFg.Width, bFg.Height); Graphics rsgr = Graphics.FromImage(bRes); //resulting bitmap's graphics if (fmtCharacter.Angle == 0) rsgr.FillRectangle(fmtParagraph.BackgroundBrush, 0, 0, size.Width, size.Height); else { Matrix trOld = rsgr.Transform.Clone(); rsgr.TranslateTransform(topLeftOffset.X, topLeftOffset.Y); rsgr.RotateTransform(fmtCharacter.Angle); rsgr.FillRectangle(fmtParagraph.BackgroundBrush, 0, 0, size.Width, size.Height); rsgr.Transform = trOld; } rsgr.DrawImage(bFg, 0, 0); rsgr.Dispose(); bFg.Dispose(); return bRes; } else { return bFg; } }
/// <summary> /// Draws string with given character format and alignment at given <see cref="Point"/>. /// </summary> /// <param name="gr"><see cref="Graphics"/> object to draw into.</param> /// <param name="str"><see cref="string"/> to be drawn.</param> /// <param name="pnt"><see cref="Point"/> where the string will be drawn.</param> /// <param name="fmt"><see cref="CharacterFormat"/> used to draw the <see cref="string"/>. </param> /// <param name="align">Specifies whether the <paramref name="pnt"/> is at left, right /// or in the center of the string. (<see cref="ParagraphAlignment.Full"/> means the same /// as <see cref="ParagraphAlignment.Left"/>.)</param> public void DrawString(Graphics gr, string str, PointF pnt, CharacterFormat fmt, ParagraphAlignment align) { StringFormat sf = (StringFormat) StringFormat.GenericDefault.Clone(); sf.Alignment = ParAl2StrAl(align); sf.HotkeyPrefix = fmt.HotkeyPrefix; Matrix trOld = null; if (fmt.Angle != 0) { trOld = gr.Transform.Clone(); gr.TranslateTransform(pnt.X, pnt.Y); gr.RotateTransform(fmt.Angle); gr.TranslateTransform(-pnt.X, -pnt.Y); } if (fmt.Formatted) { CharacterFormat cf = fmt.ShallowCopy(); cf.Angle = 0; SDUFormattedString fs = new SDUFormattedString(str, cf); SizeF sz = fs.Measure(gr, true); sz.Height += GetMeasureStringVerticalGap(gr, cf.Font); PointF ofs = pnt; switch (align) { case ParagraphAlignment.Right: ofs.X -= sz.Width; break; case ParagraphAlignment.Center: ofs.X -= sz.Width/2; break; } ParagraphFormat pf = new ParagraphFormat(); pf.MultiLine = false; pf.Alignment = align; fs.DrawStringInRectangle(gr, new RectangleF(ofs, sz), pf); } else if (fmt.FilledBounds) { ParagraphFormat pf = new ParagraphFormat(); //pf.Alignment=align; SizeF sz = MeasureStringExactly(gr, str, fmt, pf, false); sz.Width += GetMeasureStringHorizontalGap(gr, fmt.Font); pnt.X += GetMeasureStringHorizontalGap(gr, fmt.Font)/2; pnt.Y += GetMeasureStringVerticalGap(gr, fmt.Font); gr.FillRectangle(fmt.Brush, new RectangleF(pnt, sz)); } else gr.DrawString(str, fmt.Font, fmt.Brush, pnt, sf); if (trOld != null) gr.Transform = trOld; }
/// <summary> /// Paint the node text /// </summary> /// <param name="oNode"></param> /// <param name="oGraphics"></param> /// <param name="nX"></param> /// <param name="nY"></param> private void PaintNodeText(Node oNode, Graphics oGraphics, int nX, int nY, ref Hashtable m_mapRectToItemCheck, ref Hashtable m_mapItemCheckToRect) { int nAlpha = oNode.TreeView.GetNodeAlpha(oNode); Font oFont = oNode.GetFont(); if (oNode.Flash == true) nAlpha = 255; //if (oNode.GetShowPlusMinus() == false) // nX -= 15; SizeF oTextSize = oGraphics.MeasureString(oNode.GetText(), oNode.GetFont(), oNode.GetTreeView().GetDrawWidth() - nX - 8); if (m_oTreeView.Multiline == false) oTextSize = oGraphics.MeasureString(oNode.GetText(), oFont); oNode.Top = nY + (int)oTextSize.Height + 2; int nTextWidth = (int)oTextSize.Width; oNode.TextWidth = nTextWidth; if (oTextSize.Height == 0) { oTextSize.Height = oNode.GetFont().Height; } // draw check boxes if (oNode.GetCheckBoxes() == true && oNode.CheckBoxVisible == true) { Pen oPen = null; if (oNode.Flash == true) nAlpha = 255; #region draw check background if (oNode.GetCheckBackColor() != Color.Transparent) { if (oNode.Parent == null || oNode.Parent.SubNodesCheckExclusive == false) { Rectangle rectCheckBack = new Rectangle(nX + 4, nY + 2, 11, 11); Color backColor = Color.FromArgb(nAlpha, oNode.GetCheckBackColor()); Brush brush = new SolidBrush(backColor); if (oNode.GetCheckBorderStyle() == CheckBoxBorderStyle.XP) { Color fadeColor = oNode.GetCheckBackColor(); if (fadeColor == Color.White) fadeColor = Color.LightGray; brush = new LinearGradientBrush ( rectCheckBack, Color.FromArgb(nAlpha == 255 ? 255 : 48, fadeColor), Color.White, LinearGradientMode.ForwardDiagonal ); } oGraphics.FillRectangle(brush, rectCheckBack); brush.Dispose(); brush = null; } else { Rectangle rectCheckBack = new Rectangle(nX + 4, nY + 2, 11, 11); Color backColor = Color.FromArgb(nAlpha, oNode.GetCheckBackColor()); Brush brush = new SolidBrush(backColor); if (oNode.GetCheckBorderStyle() == CheckBoxBorderStyle.XP) { Color fadeColor = oNode.GetCheckBackColor(); if (fadeColor == Color.White) fadeColor = Color.LightGray; brush = new LinearGradientBrush ( rectCheckBack, Color.FromArgb(nAlpha == 255 ? 255 : 48, fadeColor), Color.White, LinearGradientMode.ForwardDiagonal ); } GraphicsPath path = new GraphicsPath(); path.AddEllipse(nX + 4, nY + 2, 10, 10); Region region = new Region(path); oGraphics.Clip = region; oGraphics.FillRectangle(brush, rectCheckBack); oGraphics.Clip = new Region(new Rectangle(0, 0, m_oTreeView.Width, m_oTreeView.Height)); brush.Dispose(); brush = null; } } #endregion oPen = new Pen(Color.FromArgb(nAlpha, oNode.GetCheckBorderColor()), 1); Rectangle rectCheck = new Rectangle(nX + 3, nY + 1, 12, 12); #region draw check rectangle if (oNode.Parent == null || oNode.Parent.SubNodesCheckExclusive == false) { if (oNode.GetCheckBorderStyle() == CheckBoxBorderStyle.Solid || oNode.GetCheckBorderStyle() == CheckBoxBorderStyle.XP) oGraphics.DrawRectangle(oPen, rectCheck); if (oNode.GetCheckBorderStyle() == CheckBoxBorderStyle.Dot) { oPen.DashStyle = DashStyle.Dot; oGraphics.DrawRectangle(oPen, rectCheck); } } else { if (oNode.GetCheckBorderStyle() == CheckBoxBorderStyle.Dot) oPen.DashStyle = DashStyle.Dot; if (oNode.GetCheckBorderStyle() == CheckBoxBorderStyle.Solid || oNode.GetCheckBorderStyle() == CheckBoxBorderStyle.XP || oNode.GetCheckBorderStyle() == CheckBoxBorderStyle.Dot) oGraphics.DrawEllipse(oPen, nX + 4, nY + 2, 10, 10); } oPen.DashStyle = DashStyle.Solid; #endregion m_mapItemCheckToRect.Add(oNode, rectCheck); m_mapRectToItemCheck.Add(rectCheck, oNode); if (oNode.Checked == true) { if (oNode.Parent == null || oNode.Parent.SubNodesCheckExclusive == false) { oPen.Color = Color.FromArgb(nAlpha, oNode.GetCheckCheckColor()); oGraphics.DrawLine(oPen, nX + 6, nY + 6, nX + 6, nY + 8); oGraphics.DrawLine(oPen, nX + 7, nY + 7, nX + 7, nY + 9); oGraphics.DrawLine(oPen, nX + 8, nY + 8, nX + 8, nY + 10); oGraphics.DrawLine(oPen, nX + 9, nY + 7, nX + 9, nY + 9); oGraphics.DrawLine(oPen, nX + 10, nY + 6, nX + 10, nY + 8); oGraphics.DrawLine(oPen, nX + 11, nY + 5, nX + 11, nY + 7); oGraphics.DrawLine(oPen, nX + 12, nY + 4, nX + 12, nY + 6); } else { oPen.Color = Color.FromArgb(nAlpha, oNode.GetCheckCheckColor()); oGraphics.DrawEllipse(oPen, nX + 6, nY + 4, 6, 6); oGraphics.DrawEllipse(oPen, nX + 7, nY + 5, 4, 4); oGraphics.DrawEllipse(oPen, nX + 8, nY + 6, 2, 2); oGraphics.DrawEllipse(oPen, nX + 9, nY + 7, 1, 1); oGraphics.DrawRectangle(oPen, nX + 8, nY + 6, 2, 2); } } oPen.Dispose(); nX += 17; } // paint the picture first if needed then draw the text if (oNode.Image != null) { oGraphics.DrawImage(oNode.Image, nX + 4, nY + (int)(oTextSize.Height / 2.0) - (int)((float)oNode.Image.Height / 2.0) + 1, oNode.Image.Width, oNode.Image.Height); nX += oNode.Image.Width + 2; } else { if (oNode.ImageIndex != -1 && oNode.TreeView.ImageList != null && oNode.ImageIndex < oNode.TreeView.ImageList.Images.Count) { oNode.TreeView.ImageList.Draw(oGraphics, nX + 2, nY + (int)(oTextSize.Height / 2.0) - (int)((float)oNode.TreeView.ImageList.ImageSize.Height / 2.0), oNode.TreeView.ImageList.ImageSize.Width, oNode.TreeView.ImageList.ImageSize.Height, oNode.ImageIndex); nX += oNode.TreeView.ImageList.ImageSize.Width; } } // get the right side of the text. if it is more far away than the width of the TreeView, truncate the text string sText = oNode.GetText(); // render the text SolidBrush oTextBrush = new SolidBrush(Color.FromArgb(nAlpha, oNode.GetForeColor())); // when draging the mouse over nodes, test if the node is being highlighted if (oNode == oNode.TreeView.HighlightedNode) oTextBrush.Color = oNode.GetHighlightedForeColor(); Rectangle textRect = new Rectangle(nX + 2, nY, oNode.GetTreeView().GetDrawWidth() - nX - 15, (int)oNode.GetTreeView().Height); SizeF textSize = oGraphics.MeasureString(StringDrawUtils.GetInstance().GetTextFromFormattedString(sText), oFont, oNode.GetTreeView().GetDrawWidth() - nX - 15); // clear the truncated flag oNode.TextTruncated = false; if (m_oTreeView.Multiline) { if (oNode.UseFormatting == true) { CharacterFormat chrFormat = new CharacterFormat( oFont, oTextBrush, 0, HotkeyPrefix.None, true); ParagraphFormat paraFormat = new ParagraphFormat( ParagraphAlignment.Left, ParagraphVerticalAlignment.Top, true, true, StringTrimming.None, Brushes.Transparent); StringDrawUtils.GetInstance().DrawStringInRectangle(oGraphics, sText, textRect, chrFormat, paraFormat); } else oGraphics.DrawString(sText, oFont, oTextBrush, textRect); } else { sText = oNode.GetText(); oTextSize = oGraphics.MeasureString(StringDrawUtils.GetInstance().GetTextFromFormattedString(sText), oFont); nTextWidth = (int)oTextSize.Width; if (nX + 2 + nTextWidth > m_oTreeView.m_LastNX) m_oTreeView.m_LastNX = nX + 2 + nTextWidth; if (nX + nTextWidth > oNode.GetTreeView().GetDrawWidth() - 15) { sText = StringDrawUtils.GetInstance().GetTextFromFormattedString(sText); float fChar = oTextSize.Width / float.Parse(sText.Length.ToString()); int nChar = (int)((float)(nX + nTextWidth - oNode.GetTreeView().GetDrawWidth() + 15) / fChar) + 4; if (nChar < 0) nChar = 0; int nLength = sText.Length - nChar; if (nLength < 0) nLength = 0; sText = sText.Substring(0, nLength) + "..."; oNode.TextTruncated = true; } // draw string (text) of the node in the proper system, based on the information whether it has formatting or not if (oNode.UseFormatting == true) { CharacterFormat chrFormat = new CharacterFormat( oFont, oTextBrush, 0, HotkeyPrefix.None, true); StringDrawUtils.GetInstance().DrawString(oGraphics, sText, new PointF(nX + 2, nY), chrFormat, ParagraphAlignment.Left); } else oGraphics.DrawString(sText, oFont, oTextBrush, nX + 2, nY); } if (m_oTreeView.Multiline == false) textSize = oGraphics.MeasureString(StringDrawUtils.GetInstance().GetTextFromFormattedString(sText), oFont); oTextBrush.Dispose(); // create the expand icon if specified if (oNode.GetShowSubitemsIndicator() == true && oNode.Nodes.Count > 0 && oNode.IsExpanded == false) { int nIconX = (int)textRect.Left + (int)textSize.Width + 4; Pen oIndicatorPen = new Pen(Color.FromArgb(nAlpha, oNode.GetForeColor()), 1); oGraphics.DrawLine(oIndicatorPen, nIconX, nY + textSize.Height - 4, nIconX + 3, nY + textSize.Height - 4); oGraphics.DrawLine(oIndicatorPen, nIconX + 1, nY + textSize.Height - 5, nIconX + 3, nY + textSize.Height - 5); oGraphics.DrawLine(oIndicatorPen, nIconX + 2, nY + textSize.Height - 6, nIconX + 3, nY + textSize.Height - 6); oGraphics.DrawLine(oIndicatorPen, nIconX + 3, nY + textSize.Height - 6, nIconX + 3, nY + textSize.Height - 7); oIndicatorPen.Dispose(); } if (oNode.Underline == true) PaintNodeUnderline(oNode, sText, oGraphics, nX, nY); }
/// <summary> /// Draws string with given character format at given <see cref="Point"/>. /// </summary> /// <param name="gr"><see cref="Graphics"/> object to draw into.</param> /// <param name="str"><see cref="string"/> to be drawn.</param> /// <param name="pnt">Top-left <see cref="Point"/> of the drawn string.</param> /// <param name="fmt"><see cref="CharacterFormat"/> used to draw the <see cref="string"/>. </param> public void DrawString(Graphics gr, string str, PointF pnt, CharacterFormat fmt) { DrawString(gr, str, pnt, fmt, ParagraphAlignment.Left); }
/// <summary> /// Initializes a new SDUFormattedString object with given string and /// initial CharacterFormat.<br/> /// Format string definition is at documentation for /// <see cref="CharacterFormat.Formatted"/> member. /// </summary> /// <param name="str"></param> /// <param name="initFmt"></param> public SDUFormattedString(string str, CharacterFormat initFmt) { if(sdu==null) sdu=StringDrawUtils.GetInstance(); m_strings=new ArrayList(); m_formats=new ArrayList(); m_softbreaks=new Hashtable(); m_initialFormat=initFmt; Debug.Assert(initFmt.Angle==0); CharacterFormat curfmt=initFmt.ShallowCopy(); curfmt.Angle=0; curfmt.Formatted=false; if(!initFmt.Formatted) { AddFormattedPiece(str,curfmt); } else { Stack brushes=new Stack(); string curstr=""; for (int i = 0; i < str.Length; i++) { if(str[i]!='#') curstr+=str[i]; else if(str[i+1]=='#') { curstr+='#'; i++; } else { if(curstr.Length>0) { AddFormattedPiece(curstr,curfmt); curstr=""; curfmt=curfmt.ShallowCopy(); } switch(str[i+1]) { case 'C': if(str.Length > i + 2 && str[i+2]=='-') { if(!initFmt.IgnoreColorFormatting) { if(brushes.Count==0) throw new ArgumentException("Invalid format string","str"); curfmt.Brush=(Brush) brushes.Pop(); } i+=2; } else if(str.Length > i + 7) { string colstr=str.Substring(i+2,6); if(colstr.Length<6) throw new ArgumentException("Invalid format string","str"); int colint; try { colint=Int32.Parse(colstr,NumberStyles.HexNumber); } catch(FormatException) { throw new ArgumentException("Invalid format string","str"); } Color clr=Color.FromArgb( (255<<24) + colint ); if(!initFmt.IgnoreColorFormatting) { brushes.Push(curfmt.Brush); curfmt.Brush=new SolidBrush(clr); } i+=7; } break; case 'B': switch(str[i+2]) { case '+': curfmt.Font=new Font(curfmt.Font,curfmt.Font.Style|FontStyle.Bold); break; case '-': curfmt.Font=new Font(curfmt.Font,curfmt.Font.Style & ~FontStyle.Bold); break; default: throw new ArgumentException("Invalid format string","str"); } i+=2; break; case 'I': switch(str[i+2]) { case '+': curfmt.Font=new Font(curfmt.Font,curfmt.Font.Style|FontStyle.Italic); break; case '-': curfmt.Font=new Font(curfmt.Font,curfmt.Font.Style & ~FontStyle.Italic); break; default: throw new ArgumentException("Invalid format string","str"); } i+=2; break; case 'U': switch(str[i+2]) { case '+': curfmt.Font=new Font(curfmt.Font,curfmt.Font.Style|FontStyle.Underline); break; case '-': curfmt.Font=new Font(curfmt.Font,curfmt.Font.Style & ~FontStyle.Underline); break; default: throw new ArgumentException("Invalid format string","str"); } i+=2; break; default: throw new ArgumentException("Invalid format string","str"); } } } if(curstr.Length>0) AddFormattedPiece(curstr,curfmt); } }
/// <summary> /// Returns Image with <paramref name="str"/> drawn into rectangle of given size. /// </summary> /// <param name="str"><see cref="string"/> to be drawn.</param> /// <param name="size">Size of rectangle string will be drawn into and also of the /// returned image.</param> /// <param name="fmtCharacter"><see cref="CharacterFormat"/> used to draw the <paramref name="str"/>. </param> /// <param name="fmtParagraph"><see cref="ParagraphFormat"/> used to draw the <paramref name="str"/>. </param> /// <returns><see cref="Image"/> with given <paramref name="str"/> drawn.</returns> public Image DrawStringInRectangle(string str, SizeF size, CharacterFormat fmtCharacter, ParagraphFormat fmtParagraph) { PointF tmp; return DrawStringInRectangle(str, size, fmtCharacter, fmtParagraph, out tmp); }