Skip to content

Commit bce7f9f

Browse files
authored
Merge pull request #2339 from EPPlusSoftware/fix/user-friendly-linefragment
Fix/user friendly linefragment
2 parents f9c0eb0 + 208d6c4 commit bce7f9f

30 files changed

Lines changed: 1433 additions & 809 deletions

src/EPPlus.Export.ImageRenderer.Test/TextboxTest.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,24 @@ public void TextBoxVerification()
4949

5050
item.ExternalRenderItemsNoBounds.Add(txtBoxNotMaxed);
5151

52+
var sb = new StringBuilder();
53+
54+
item.Render(sb);
55+
var svgString = sb.ToString();
56+
57+
//before we assumed we consider the space widths
58+
var widthWithSpace = txtBox.TextBody.Paragraphs[0].SpaceWidthsPerLine[0] + txtBox.Width;
59+
60+
5261
//Assert.AreEqual(36d, txtBox.TextBody.Width);
53-
Assert.AreEqual(37.5d, txtBox.Width, 0.5);
62+
Assert.AreEqual(37.5d, widthWithSpace, 0.5);
5463
Assert.AreNotEqual(36d, txtBoxNotMaxed.Width);
5564
Assert.AreEqual(16.29052734375d, txtBoxNotMaxed.Width);
5665

57-
var sb = new StringBuilder();
66+
//var sb = new StringBuilder();
5867

59-
item.Render(sb);
60-
var svgString = sb.ToString();
68+
//item.Render(sb);
69+
//var svgString = sb.ToString();
6170

6271
SaveTextFileToWorkbook($"svg\\StandAloneTextBox.svg", svgString);
6372
}

src/EPPlus.Export.ImageRenderer/RenderItems/Shared/ParagraphItem.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ internal abstract class ParagraphItem : RenderItem
4646

4747
private double? _centerAdjustment = null;
4848

49+
internal List<double> SpaceWidthsPerLine = new List<double>();
50+
4951
public ParagraphItem(TextBodyItem textBody, DrawingBase renderer, BoundingBox parent) : base(renderer, parent)
5052
{
5153
ParentTextBody = textBody;
@@ -294,12 +296,16 @@ internal void AddLinesAndTextRuns(string textIfEmpty)
294296
//START
295297
var idxOfLargestLine = 0;
296298
double widthOfLargestLine = lines[0].Width;
299+
//SpaceWidthsPerLine.Add(lines[0].lastFontSpaceWidth);
300+
297301

298302
for (int i = 1; i < lines.Count; i++)
299303
{
300304
if (lines[i].Width > widthOfLargestLine)
301305
{
302306
var ctrLineWidth = lines[i].GetWidthWithoutTrailingSpaces();
307+
SpaceWidthsPerLine.Add(lines[i].lastFontSpaceWidth);
308+
303309
widthOfLargestLine = ctrLineWidth;
304310
idxOfLargestLine = i;
305311
}
@@ -345,7 +351,7 @@ internal void AddLinesAndTextRuns(string textIfEmpty)
345351

346352
foreach (var lineFragment in line.LineFragments)
347353
{
348-
var displayText = line.GetLineFragmentText(lineFragment);
354+
var displayText = lineFragment.Text;
349355

350356
if (string.IsNullOrEmpty(textIfEmpty) == false)
351357
{
@@ -408,12 +414,14 @@ private void AddLinesAndTextRuns(ExcelDrawingParagraph p, string textIfEmpty)
408414
//START
409415
var idxOfLargestLine = 0;
410416
double widthOfLargestLine = lines[0].GetWidthWithoutTrailingSpaces();
417+
//SpaceWidthsPerLine.Add(lines[0].lastFontSpaceWidth);
411418

412419
for (int i = 1; i < lines.Count; i++)
413420
{
414421
if (lines[i].Width > widthOfLargestLine)
415422
{
416423
var ctrLineWidth = lines[i].GetWidthWithoutTrailingSpaces();
424+
SpaceWidthsPerLine.Add(lines[i].lastFontSpaceWidth);
417425
widthOfLargestLine = ctrLineWidth;
418426
idxOfLargestLine = i;
419427
}
@@ -460,15 +468,16 @@ private void AddLinesAndTextRuns(ExcelDrawingParagraph p, string textIfEmpty)
460468

461469
foreach (var lineFragment in line.LineFragments)
462470
{
463-
var displayText = line.GetLineFragmentText(lineFragment);
471+
var displayText = lineFragment.Text;
464472

465473
if (p.TextRuns.Count == 0 && string.IsNullOrEmpty(textIfEmpty) == false)
466474
{
467475
AddText(displayText, p.DefaultRunProperties, prevWidth);
468476
}
469477
else
470478
{
471-
AddRenderItemTextRun(p.TextRuns[lineFragment.RtFragIdx], displayText, prevWidth);
479+
var idx = _newTextFragments.IndexOf(lineFragment.OriginalTextFragment);
480+
AddRenderItemTextRun(p.TextRuns[idx], displayText, prevWidth);
472481
}
473482

474483
TextRunItem runItem = Runs.Last();

src/EPPlus.Export.Pdf.Tests/PdfTests.cs

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,19 @@ Date Author Change
1010
*************************************************************************************************
1111
10/07/2025 EPPlus Software AB EPPlus.Fonts.OpenType 1.0
1212
*************************************************************************************************/
13+
using EPPlus.Export.Pdf;
14+
using EPPlus.Export.Pdf.PdfLayout;
15+
using EPPlus.Export.Pdf.PdfSettings;
16+
using EPPlus.Export.Pdf.PdfSettings.PdfPageSizes;
17+
using EPPlus.Fonts.OpenType;
18+
using EPPlus.Fonts.OpenType.Integration;
19+
using OfficeOpenXml.Interfaces.Drawing.Text;
20+
using OfficeOpenXml.Style;
1321
using System;
1422
using System.IO;
1523
using System.Linq;
1624
using System.Text;
1725
using System.Xml.Linq;
18-
using EPPlus.Export.Pdf;
19-
using EPPlus.Export.Pdf.PdfSettings;
20-
using EPPlus.Export.Pdf.PdfSettings.PdfPageSizes;
2126

2227
namespace EPPlusTest.PDF
2328
{
@@ -125,5 +130,79 @@ public void TestWritePdf2()
125130
ExcelPdf pedeef = new ExcelPdf(p.Workbook.Worksheets.First(), pageSettings);
126131
pedeef.CreatePdf("c:\\epplustest\\pdf\\EmojiTest.pdf");
127132
}
133+
134+
[TestMethod]
135+
public void WritePrintAreas()
136+
{
137+
using var p = OpenTemplatePackage("PDFTest.xlsx");
138+
var cell = p.Workbook.Worksheets[0].Cells["P118"];
139+
140+
List<TextFragment> TextFragments = GetTextFragments(cell.RichText);
141+
142+
143+
var layout = OpenTypeFonts.GetTextLayoutEngineForFont(TextFragments[0].Font);
144+
145+
var TextLines = layout.WrapRichTextLineCollection(TextFragments, 51d);
146+
////var ShapedTexts = new List<PdfShapedText>();
147+
//for (int i = 0; i < TextFragments.Count; i++)
148+
//{
149+
// var tf = TextFragments[i];
150+
// var layout = OpenTypeFonts.GetTextLayoutEngineForFont(TextFragments[i].Font);
151+
152+
// var TextLines = layout.WrapRichTextLineCollection(TextFragments, 51d);
153+
//}
154+
155+
}
156+
157+
private static List<TextFragment> GetTextFragments(ExcelRichTextCollection RichTextCollection, PdfCellStyle cellStyle = null)
158+
{
159+
var textFragments = new List<TextFragment>();
160+
bool bold = false, italic = false, underline = false, strike = false;
161+
ExcelUnderLineType underLineType = ExcelUnderLineType.None;
162+
if (cellStyle != null && cellStyle.dxfFont != null)
163+
{
164+
bold = cellStyle.dxfFont.Bold != null ? (bool)cellStyle.dxfFont.Bold : false;
165+
italic = cellStyle.dxfFont.Italic != null ? (bool)cellStyle.dxfFont.Italic : false;
166+
strike = cellStyle.dxfFont.Strike != null ? (bool)cellStyle.dxfFont.Strike : false;
167+
underline = cellStyle.dxfFont.Underline != null;
168+
underLineType = cellStyle.dxfFont.Underline != null ? (ExcelUnderLineType)cellStyle.dxfFont.Underline : ExcelUnderLineType.None;
169+
}
170+
for (int i = 0; i < RichTextCollection.Count; i++)
171+
{
172+
var rt = RichTextCollection[i];
173+
var textFrag = new TextFragment();
174+
textFrag.Font = new MeasurementFont();
175+
textFrag.Text = rt.Text;
176+
177+
textFrag.Font.FontFamily = rt.FontName;
178+
textFrag.Font.Size = rt.Size;
179+
180+
textFrag.RichTextOptions.IsBold = rt.Bold || bold;
181+
textFrag.RichTextOptions.IsItalic = rt.Italic || italic;
182+
//underline
183+
//none : 12
184+
//single : 13
185+
//Double : 4
186+
//accouting does not exsist
187+
textFrag.RichTextOptions.UnderlineType = 12;
188+
textFrag.RichTextOptions.UnderlineType = rt.UnderLineType == ExcelUnderLineType.Single ? 13 : textFrag.RichTextOptions.UnderlineType;
189+
textFrag.RichTextOptions.UnderlineType = rt.UnderLineType == ExcelUnderLineType.Double ? 4 : textFrag.RichTextOptions.UnderlineType;
190+
textFrag.RichTextOptions.StrikeType = rt.Strike || strike ? 2 : 1;
191+
textFrag.RichTextOptions.SuperScript = rt.VerticalAlign == ExcelVerticalAlignmentFont.Superscript;
192+
textFrag.RichTextOptions.SubScript = rt.VerticalAlign == ExcelVerticalAlignmentFont.Subscript;
193+
textFrag.RichTextOptions.FontColor = rt.Color;
194+
195+
textFrag.Font.Style = (textFrag.RichTextOptions.IsBold ? MeasurementFontStyles.Bold : 0) |
196+
(textFrag.RichTextOptions.IsItalic ? MeasurementFontStyles.Italic : 0) |
197+
(textFrag.RichTextOptions.UnderlineType != 12 ? MeasurementFontStyles.Underline : 0) |
198+
(textFrag.RichTextOptions.StrikeType > 1 ? MeasurementFontStyles.Strikeout : 0);
199+
200+
201+
textFragments.Add(textFrag);
202+
OpenTypeFonts.GetFontSubFamily(textFrag.Font.Style);
203+
}
204+
205+
return textFragments;
206+
}
128207
}
129208
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
using EPPlus.Fonts.OpenType.Integration;
2+
using EPPlus.Fonts.OpenType.TextShaping;
3+
using EPPlus.Fonts.OpenType.Utils;
4+
using OfficeOpenXml.Interfaces.Drawing.Text;
5+
using OfficeOpenXml.Style;
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Drawing;
9+
using System.Linq;
10+
using System.Text;
11+
using System.Threading.Tasks;
12+
13+
namespace EPPlus.Fonts.OpenType.Tests.DataHolders
14+
{
15+
[TestClass]
16+
public class TextLineSimpleTests
17+
{
18+
[TestMethod]
19+
public void TestLineFragmentAbstraction()
20+
{
21+
var maxSizePoints = Math.Round(300d, 0, MidpointRounding.AwayFromZero).PixelToPoint();
22+
23+
var fragments = GetTextFragments();
24+
25+
var layout = OpenTypeFonts.GetTextLayoutEngineForFont(fragments[0].Font);
26+
var wrappedLines = layout.WrapRichTextLines(fragments, maxSizePoints);
27+
var wrappedCollection = layout.WrapRichTextLineCollection(fragments, maxSizePoints);
28+
29+
30+
}
31+
32+
[TestMethod]
33+
public void TestLineFragmentSeeWhatLinesUseWhatRichText()
34+
{
35+
var maxSizePoints = Math.Round(300d, 0, MidpointRounding.AwayFromZero).PixelToPoint();
36+
37+
var fragments = GetTextFragments();
38+
39+
fragments[4].RichTextOptions.FontColor = Color.DarkRed;
40+
41+
var layout = OpenTypeFonts.GetTextLayoutEngineForFont(fragments[0].Font);
42+
43+
var wrappedLines = layout.WrapRichTextLines(fragments, maxSizePoints);
44+
var wrappedCollection = layout.WrapRichTextLineCollection(fragments, maxSizePoints);
45+
46+
var lines = wrappedCollection.GetTextLinesThatUse(fragments[4]);
47+
var specificFragments = wrappedCollection.GetLineFragmentsThatUse(fragments[4]);
48+
var lineIndicies = wrappedCollection.GetLineNumbersThatUse(fragments[4]);
49+
50+
Assert.AreEqual(lines[0].LineFragments[1], specificFragments[0]);
51+
Assert.AreEqual(fragments[4], wrappedCollection.LineFragments[6].OriginalTextFragment);
52+
Assert.AreEqual(Color.DarkRed, wrappedCollection.LineFragments[6].OriginalTextFragment.RichTextOptions.FontColor);
53+
54+
var expectedArr = new int[] { 3, 4 };
55+
expectedArr.SequenceCompareTo(lineIndicies);
56+
}
57+
58+
List<TextFragment> GetTextFragments()
59+
{
60+
List<string> lstOfRichText = new() { "TextBox\r\na\r\n", "TextBox2", "ra underline", "La Strike", "Goudy size 16", "SvgSize 24" };
61+
62+
var font1 = new MeasurementFont()
63+
{
64+
FontFamily = "Aptos Narrow",
65+
Size = 11,
66+
Style = MeasurementFontStyles.Regular
67+
};
68+
69+
var font2 = new MeasurementFont()
70+
{
71+
FontFamily = "Aptos Narrow",
72+
Size = 11,
73+
Style = MeasurementFontStyles.Bold
74+
};
75+
76+
var font3 = new MeasurementFont()
77+
{
78+
FontFamily = "Aptos Narrow",
79+
Size = 11,
80+
Style = MeasurementFontStyles.Underline
81+
};
82+
83+
var font4 = new MeasurementFont()
84+
{
85+
FontFamily = "Aptos Narrow",
86+
Size = 11,
87+
Style = MeasurementFontStyles.Strikeout
88+
};
89+
90+
var font5 = new MeasurementFont()
91+
{
92+
FontFamily = "Goudy Stout",
93+
Size = 16,
94+
Style = MeasurementFontStyles.Regular
95+
};
96+
97+
98+
var font6 = new MeasurementFont()
99+
{
100+
FontFamily = "Aptos Narrow",
101+
Size = 24,
102+
Style = MeasurementFontStyles.Regular
103+
};
104+
105+
List<MeasurementFont> fonts = new() { font1, font2, font3, font4, font5, font6 };
106+
var fragments = new List<TextFragment>();
107+
108+
for (int i = 0; i < lstOfRichText.Count(); i++)
109+
{
110+
var currentFrag = new TextFragment() { Text = lstOfRichText[i], Font = fonts[i] };
111+
fragments.Add(currentFrag);
112+
}
113+
114+
return fragments;
115+
}
116+
}
117+
}

0 commit comments

Comments
 (0)