internal static void FitOneLine(IFlxGraphics Canvas, TFontCache FontCache, real Zoom100, TRichString Text, Font AFont, real w, int idx, ref int fit, ref real StrWidth) { if (StrWidth > w) { real md = 0; fit = Math.Max((int)Math.Round(fit * w / StrWidth), 1); CharUtils.SameOrMore(Text.Value, idx, ref fit); StrWidth = CalcTextExtent(Canvas, FontCache, Zoom100, AFont, Text.Substring(idx, fit), out md).Width; if (StrWidth > w) { while (fit > 1 && StrWidth > w) { fit--; CharUtils.SameOrLess(Text.Value, idx, ref fit); StrWidth = CalcTextExtent(Canvas, FontCache, Zoom100, AFont, Text.Substring(idx, fit), out md).Width; } } else { while (StrWidth < w) //Will not overflow, as wc>w for fit+idx=MaxLength. { int fit1 = fit + 1; CharUtils.SameOrMore(Text.Value, idx, ref fit1); StrWidth = CalcTextExtent(Canvas, FontCache, Zoom100, AFont, Text.Substring(idx, fit1), out md).Width; if (StrWidth < w) { fit = fit1; } } } } }
internal TGraphicCanvas() { FontCache = new TFontCache(); bmp = BitmapConstructor.CreateBitmap(1, 1); imgData = Graphics.FromImage(bmp); imgData.PageUnit = GraphicsUnit.Point; Canvas = new GdiPlusGraphics(imgData); Canvas.CreateSFormat(); }
internal static void AddWilcardtoLine(IFlxGraphics Canvas, TFontCache FontCache, real Zoom100, Font AFont, real MaxTextWidth, ref SizeF TextExtent, TXRichString LineWithWildcard) { if (LineWithWildcard == null || LineWithWildcard.AdaptFormat == null) { return; } int WildcardPos = LineWithWildcard.AdaptFormat.WildcardPos; if (WildcardPos < 0) { return; } string s1 = LineWithWildcard.s.ToString(); string WildcardChar = String.Empty + s1[WildcardPos]; if (CharUtils.IsSurrogatePair(s1, WildcardPos) && WildcardPos + 1 < s1.Length) { WildcardChar += s1[WildcardPos + 1]; } string sOrg = s1; s1 = s1.Remove(WildcardPos, WildcardChar.Length);//consider the case the string has 0 wildcards. string sOld = s1; real wc; real OldWc = LineWithWildcard.XExtent; do { real md; wc = RenderMetrics.CalcTextExtent(Canvas, FontCache, Zoom100, AFont, new TRichString(s1), out md).Width; if (wc < MaxTextWidth) { sOld = s1; OldWc = wc; s1 = s1.Insert(WildcardPos, WildcardChar); } }while (wc < MaxTextWidth); LineWithWildcard.s = new TRichString(sOld); if (sOrg.Length > sOld.Length) { LineWithWildcard.AdaptFormat.RemovedPosition(WildcardPos, sOrg.Length - sOld.Length); //a line with a wildcard can have 0 characters in the wildcard. } if (sOrg.Length < sOld.Length) { LineWithWildcard.AdaptFormat.InsertedPosition(WildcardPos, sOld.Length - sOrg.Length); } LineWithWildcard.XExtent = OldWc; if (TextExtent.Width < OldWc) { TextExtent.Width = OldWc; } }
private static void WriteJustText(ExcelFile Workbook, IFlxGraphics Canvas, TFontCache FontCache, real Zoom100, bool ReverseRightToLeftStrings, Font AFont, Color AFontColor, real y, real SubOfs, RectangleF CellRect, real Clp, TRichString OutText, real XExtent, real MaxDescent, bool Distributed, TAdaptativeFormats AdaptativeFormats) { List <TRichString> Words = new List <TRichString>(); OutText = GetVisualString(OutText, ReverseRightToLeftStrings); string s = OutText.Value; int p = 0; int p1 = 0; while ((p1 = s.IndexOf(' ', p)) >= 0) { Words.Add(OutText.Substring(p, p1 - p + 1)); p = p1 + 1; } if (p < s.Length) { Words.Add(OutText.Substring(p)); } real md; real wc = XExtent; // CalcTextExtent(AFont, OutText, out md).Width; real Spaces = 0; if (Words.Count - 1 > 0) { Spaces = (CellRect.Width - 2 - 2 * Clp - wc) / (Words.Count - 1); } real x = CellRect.Left + 1 + Clp; if (Words.Count == 1 && Distributed) //Center when it is one word { x = (CellRect.Left + CellRect.Right - 2 - 2 * Clp - wc) / 2; } for (int i = 0; i < Words.Count; i++) { TRichString wo = Words[i]; WriteText(Workbook, Canvas, FontCache, Zoom100, AFont, AFontColor, x, y, SubOfs, wo, 0, MaxDescent, AdaptativeFormats); x += RenderMetrics.CalcTextExtent(Canvas, FontCache, Zoom100, AFont, wo, out md).Width; if (Spaces > 0) { x += Spaces; } } }
internal static void CalcTextBox(IFlxGraphics Canvas, TFontCache FontCache, real Zoom100, RectangleF CellRect, real Clp, bool MultiLine, real Alpha, bool Vertical, TRichString OutText, Font AFont, TAdaptativeFormats AdaptativeFormats, out SizeF TextExtent, out TXRichStringList TextLines, out TFloatList MaxDescent) { TextExtent = new SizeF(0, 0); TextLines = new TXRichStringList(); MaxDescent = new TFloatList(); real MaxTextWidth; if (MultiLine || Vertical) { real Md; real TextHeight = RenderMetrics.CalcTextExtent(Canvas, FontCache, Zoom100, AFont, new TRichString("M"), out Md).Height; //not perfect since the rich string might have different fonts, but a good approx. MaxTextWidth = CalcMaxTextWidth(ref CellRect, Clp, Alpha, TextHeight); RenderMetrics.SplitText(Canvas, FontCache, Zoom100, OutText, AFont, MaxTextWidth, TextLines, out TextExtent, Vertical, MaxDescent, AdaptativeFormats); } else { TextLines.Add(new TXRichString(OutText, false, 0, 0, AdaptativeFormats)); real mdx = 0; TextExtent = RenderMetrics.CalcTextExtent(Canvas, FontCache, Zoom100, AFont, OutText, out mdx); TextLines[0].XExtent = TextExtent.Width; TextLines[0].YExtent = TextExtent.Height; MaxTextWidth = CalcMaxTextWidth(ref CellRect, Clp, Alpha, TextExtent.Height); MaxDescent.Add(mdx); } if (AdaptativeFormats != null && AdaptativeFormats.WildcardPos >= 0) { MaxTextWidth -= 2 * Clp; //Add some extra clipping so the text doesn't go through the line. if (Vertical) { } else { AddWildcard(Canvas, FontCache, Zoom100, AFont, MaxTextWidth, TextLines, ref TextExtent); } } }
private static void AddWildcard(IFlxGraphics Canvas, TFontCache FontCache, real Zoom100, Font AFont, real MaxTextWidth, TXRichStringList TextLines, ref SizeF TextExtent) { TXRichString LineWithWildcard = null; foreach (TXRichString line in TextLines) { if (line.s == null || line.s.RTFRunCount > 0) { return; //wildcards only apply to non formatted lines. } if (line.AdaptFormat != null && line.AdaptFormat.WildcardPos >= 0) { LineWithWildcard = line; break; } } if (LineWithWildcard == null) { return; } AddWilcardtoLine(Canvas, FontCache, Zoom100, AFont, MaxTextWidth, ref TextExtent, LineWithWildcard); }
internal static void DrawRichText(ExcelFile Workbook, IFlxGraphics Canvas, TFontCache FontCache, real Zoom100, bool ReverseRightToLeftStrings, ref RectangleF CellRect, ref RectangleF PaintClipRect, ref RectangleF TextRect, ref RectangleF ContainingRect, real Clp, THFlxAlignment HJustify, TVFlxAlignment VJustify, real Alpha, Color DrawFontColor, TSubscriptData SubData, TRichString OutText, SizeF TextExtent, TXRichStringList TextLines, Font AFont, TFloatList MaxDescent, real[] X, real[] Y) { RectangleF FinalRect = CellRect; if (ContainingRect.Right > PaintClipRect.Left && ContainingRect.Left < PaintClipRect.Right && Intersect(TextRect, PaintClipRect, out FinalRect)) { real dy = 0; int TextLinesCount = TextLines.Count; if (VJustify == TVFlxAlignment.justify || VJustify == TVFlxAlignment.distributed) { if (TextLinesCount > 1) { dy = (CellRect.Height - TextExtent.Height - 2 * Clp) / (TextLinesCount - 1); } } float eps = 0.0001F; //Tolerance for the comparison, these are floats, not doubles, and we get rounding errors soon. if (TextRect.Left - eps <= ContainingRect.Left && TextRect.Right + eps >= ContainingRect.Right && TextRect.Top - eps <= ContainingRect.Top && TextRect.Bottom + eps >= ContainingRect.Bottom) { Canvas.SetClipReplace(PaintClipRect); //This will improve pdf export a lot, since we can now join all the BT/ET tags inside one, and then we can also join fonts inside the ET/BT tags. } else { if (Alpha == 0 || Alpha == 90) { Canvas.SetClipReplace(FinalRect); } else { Canvas.SetClipReplace(RectangleF.FromLTRB(PaintClipRect.Left, FinalRect.Top, PaintClipRect.Right, FinalRect.Bottom)); //rotated text can move to other used cells horizontally. } } real AcumDy = 0; for (int i = 0; i < TextLinesCount; i++) { TXRichString TextLine = TextLines[i]; if ((Alpha == 0) && (HJustify == THFlxAlignment.justify && TextLine.Split) || (HJustify == THFlxAlignment.distributed) ) { Canvas.SetClipReplace(FinalRect); WriteJustText(Workbook, Canvas, FontCache, Zoom100, ReverseRightToLeftStrings, AFont, DrawFontColor, Y[i] + AcumDy, SubData.Offset(Canvas, AFont), CellRect, Clp, TextLine.s, TextLine.XExtent, MaxDescent[i], HJustify == THFlxAlignment.distributed, TextLine.AdaptFormat); } else { WriteText(Workbook, Canvas, FontCache, Zoom100, AFont, DrawFontColor, X[i], Y[i] + AcumDy, SubData.Offset(Canvas, AFont), GetVisualString(TextLine.s, ReverseRightToLeftStrings), Alpha, MaxDescent[i], TextLine.AdaptFormat); } AcumDy += dy; } } }
internal static void WriteText(ExcelFile Workbook, IFlxGraphics Canvas, TFontCache FontCache, real Zoom100, Font AFont, Color AFontColor, real x, real y, real SubOfs, TRichString OutText, real Alpha, real MaxDescent, TAdaptativeFormats AdaptativeFormats) { if (OutText.Length == 0) { return; } if (Alpha != 0) { Canvas.SaveTransform(); } try { if (Alpha != 0) { Canvas.Rotate(x, y, Alpha); } using (Brush TextBrush = new SolidBrush(AFontColor)) { if (OutText.RTFRunCount == 0) { if (AdaptativeFormats == null || AdaptativeFormats.Separators == null || AdaptativeFormats.Separators.Length == 0) //formats are only applied to non rich text cells. { Canvas.DrawString(OutText.Value, AFont, TextBrush, x, y + SubOfs); } else { DrawAdaptativeString(Canvas, AdaptativeFormats, OutText.Value, AFont, TextBrush, x, y + SubOfs); } } else { SizeF Result; string s1 = OutText.Value.Substring(0, OutText.RTFRun(0).FirstChar); if (s1.Length > 0) { Result = Canvas.MeasureString(s1, AFont, new TPointF(x, y)); Canvas.DrawString(s1, AFont, TextBrush, x, y + SubOfs - (MaxDescent - Canvas.FontDescent(AFont))); x += Result.Width; } for (int i = 0; i < OutText.RTFRunCount - 1; i++) { TFlxFont Fx = OutText.GetFont(OutText.RTFRun(i).FontIndex); TSubscriptData Sub = new TSubscriptData(Fx.Style); Font MyFont = FontCache.GetFont(Fx, Zoom100 * Sub.Factor); { using (Brush MyBrush = new SolidBrush(GetColor(Workbook, Fx.Color))) { int Start = OutText.RTFRun(i).FirstChar; if (Start >= OutText.Length) { Start = OutText.Length; } int Len = OutText.RTFRun(i + 1).FirstChar; if (Len >= OutText.Length) { Len = OutText.Length; } Len -= Start; string s2 = OutText.Value.Substring(Start, Len); Result = Canvas.MeasureString(s2, MyFont, new TPointF(x, y)); Canvas.DrawString(s2, MyFont, MyBrush, x, y + Sub.Offset(Canvas, MyFont) - (MaxDescent - Canvas.FontDescent(MyFont))); x += Result.Width; } } } TFlxFont Fy = OutText.GetFont(OutText.RTFRun(OutText.RTFRunCount - 1).FontIndex); TSubscriptData Suby = new TSubscriptData(Fy.Style); Font MyFont2 = FontCache.GetFont(Fy, Zoom100 * Suby.Factor); { using (Brush MyBrush = new SolidBrush(GetColor(Workbook, Fy.Color))) { int Start = OutText.RTFRun(OutText.RTFRunCount - 1).FirstChar; if (Start >= OutText.Length) { Start = OutText.Length; } string s3 = OutText.Value.Substring(Start); Result = Canvas.MeasureString(s3, MyFont2, new TPointF(x, y)); Canvas.DrawString(s3, MyFont2, MyBrush, x, y + Suby.Offset(Canvas, MyFont2) - (MaxDescent - Canvas.FontDescent(MyFont2))); } } } } } finally { if (Alpha != 0) { Canvas.ResetTransform(); } } }
internal static void SplitText(IFlxGraphics Canvas, TFontCache FontCache, real Zoom100, TRichString Text, Font AFont, real w, TXRichStringList TextLines, out SizeF TextExtent, bool Vertical, TFloatList MaxDescent, TAdaptativeFormats AdaptFormat) { TextLines.Clear(); MaxDescent.Clear(); int idx = 0; int fit = 0; TextExtent = new SizeF(0, 0); if (w <= 0 || Text.Value.Length <= 0) { return; } while (idx < Text.Value.Length) { int Enter = Text.Value.IndexOf((char)0x0A, idx); int MaxLength = Text.Value.Length - idx; if (Enter >= 0) { MaxLength = Enter - idx; } if (Vertical) { fit = 1; CharUtils.SameOrMore(Text.Value, idx, ref fit); } else { //Not a really efficient way, but... //First Guess. whole string. fit = MaxLength; real md; real wc = CalcTextExtent(Canvas, FontCache, Zoom100, AFont, Text.Substring(idx, fit), out md).Width; FitOneLine(Canvas, FontCache, Zoom100, Text, AFont, w, idx, ref fit, ref wc); if (fit <= 0) { fit = 1; //avoid infinite loop } CharUtils.SameOrMore(Text.Value, idx, ref fit); if (Text.Value.IndexOf(' ', idx, fit) >= 0) { int minfit = 1; CharUtils.SameOrMore(Text.Value, idx, ref minfit); while (fit > minfit && fit < MaxLength && Text.Value[idx + fit] != ' ') { fit--; } } while (fit < MaxLength && Text.Value[idx + fit] == ' ') { fit++; } //No need to adjust fit for surrogates. it will always be at the start of one. } //int Enter=Text.Value.IndexOf((char)0x0A, idx, fit); //if (Enter>0) fit=Enter-idx; int TextLen = Math.Min(MaxLength, fit); TextLines.Add(new TXRichString(Text.Substring(idx, TextLen), true, 0, 0, TAdaptativeFormats.CopyTo(AdaptFormat, idx, TextLen))); if (fit + idx < Text.Value.Length && Text.Value[idx + fit] == (char)0x0A) { TextLines[TextLines.Count - 1].Split = false; if (idx + fit < Text.Value.Length - 1) //An Enter at the end behaves different, it means we have a new empty line. { idx++; } } if (fit + idx >= Text.Value.Length) { TextLines[TextLines.Count - 1].Split = false; } idx += fit; //Recalculate dx real mdx = 0; TRichString sx = TextLines[TextLines.Count - 1].s; SizeF bSize = CalcTextExtent(Canvas, FontCache, Zoom100, AFont, sx, out mdx); if (bSize.Height == 0) { real mdx3; SizeF bSize3 = CalcTextExtent(Canvas, FontCache, Zoom100, AFont, new TRichString("M"), out mdx3); bSize.Height = bSize3.Height; } SizeF bSize2 = bSize; if (sx != null && sx.Length > 0 && sx.ToString()[sx.Length - 1] == ' ') //This is to right align line with spaces at the end. { real mdx2; bSize2 = CalcTextExtent(Canvas, FontCache, Zoom100, AFont, sx.RightTrim(), out mdx2); } TextLines[TextLines.Count - 1].XExtent = bSize2.Width; TextLines[TextLines.Count - 1].YExtent = bSize.Height; // not bSize2.Height; This might be even 0. MaxDescent.Add(mdx); if (TextExtent.Width < bSize.Width) { TextExtent.Width = bSize.Width; } TextExtent.Height += bSize.Height; } //while }
internal static SizeF CalcTextExtent(IFlxGraphics Canvas, TFontCache FontCache, real Zoom100, Font AFont, TRichString Text, out real MaxDescent) { MaxDescent = Canvas.FontDescent(AFont); if (Text.RTFRunCount == 0) { return(Canvas.MeasureStringEmptyHasHeight(Text.Value, AFont)); } real x = 0; real y = 0; SizeF Result; Result = Canvas.MeasureString(Text.Value.Substring(0, Text.RTFRun(0).FirstChar), AFont, new TPointF(0, 0)); x = Result.Width; y = Result.Height; for (int i = 0; i < Text.RTFRunCount - 1; i++) { TFlxFont Fx = Text.GetFont(Text.RTFRun(i).FontIndex); TSubscriptData Sub = new TSubscriptData(Fx.Style); Font MyFont = FontCache.GetFont(Fx, Zoom100 * Sub.Factor); { int Start = Text.RTFRun(i).FirstChar; if (Start >= Text.Length) { Start = Text.Length; } int Len = Text.RTFRun(i + 1).FirstChar; if (Len >= Text.Length) { Len = Text.Length; } Len -= Start; if (Len < 0) { continue; //wrong file, (i+1)FirstChar < (i).FirstChar } Result = Canvas.MeasureString(Text.Value.Substring(Start, Len), MyFont, new TPointF(0, 0)); x += Result.Width; y = Math.Max(y + Sub.Offset(Canvas, MyFont), Result.Height); MaxDescent = Math.Max(MaxDescent, Canvas.FontDescent(MyFont) + Sub.Offset(Canvas, MyFont)); } } TFlxFont Fy = Text.GetFont(Text.RTFRun(Text.RTFRunCount - 1).FontIndex); TSubscriptData Suby = new TSubscriptData(Fy.Style); Font MyFont2 = FontCache.GetFont(Fy, Zoom100 * Suby.Factor); { int Start = Text.RTFRun(Text.RTFRunCount - 1).FirstChar; if (Start >= Text.Length) { Start = Text.Length; } Result = Canvas.MeasureStringEmptyHasHeight(Text.Value.Substring(Start), MyFont2); x += Result.Width; y = Math.Max(y + Suby.Offset(Canvas, MyFont2), Result.Height); MaxDescent = Math.Max(MaxDescent, Canvas.FontDescent(MyFont2) + Suby.Offset(Canvas, MyFont2)); } return(new SizeF(x, y)); }