25 public class FormattedText : IFormattableText
28 private const string SI_TAG =
"si";
29 private const string R_TAG =
"r";
30 private const string T_TAG =
"t";
31 private const string RPR_TAG =
"rPr";
32 private const string RPH_TAG =
"rPh";
33 private const string PHONETIC_PR_TAG =
"phoneticPr";
34 private const string PRESERVE_ATTRIBUTE_NAME =
"space";
35 private const string PRESERVE_ATTRIBUTE_PREFIX =
"xml";
36 private const string PRESERVE_ATTRIBUTE_VALUE =
"preserve";
46 static FormattedText()
49 LineBreakStyle.CurrentCellXf.Alignment = CellXf.TextBreakValue.WrapText;
54 private readonly List<TextRun> runs =
new List<TextRun>();
55 private readonly List<PhoneticRun> phoeticRuns =
new List<PhoneticRun>();
56 private string overridePlainText =
null;
69 public IReadOnlyList<TextRun>
Runs => runs.AsReadOnly();
73 public IReadOnlyList<PhoneticRun>
PhoneticRuns => phoeticRuns.AsReadOnly();
82 public string PlainText => overridePlainText !=
null ? overridePlainText :
string.Concat(runs.Select(r => r.Text));
93 public FormattedText
AddRun(
string text, Font fontStyle =
null)
95 if (
string.IsNullOrEmpty(text))
97 throw new FormatException(
"Text cannot be null or empty");
100 runs.Add(
new TextRun(text, fontStyle));
110 public FormattedText
AddRun(
string text, Action<InlineStyleBuilder> styleBuilder)
112 if (
string.IsNullOrEmpty(text))
114 throw new FormatException(
"Text cannot be null or empty");
118 styleBuilder?.Invoke(builder);
119 runs.Add(
new TextRun(text, builder.Build()));
129 if (runs.Count == 0 || !useStyleFromLastRun)
131 runs.Add(
new TextRun(Environment.NewLine,
null));
135 runs[runs.Count - 1].Text += Environment.NewLine;
149 phoeticRuns.Add(
new PhoneticRun(text, startBase, endBase));
165 Alignment = alignment
178 overridePlainText =
null;
187 FormattedText copy =
new FormattedText();
190 copy.runs.Add(run.
Copy());
194 copy.phoeticRuns.Add(phoneticRun.
Copy());
200 copy.overridePlainText = overridePlainText;
217 internal void OverridePlainText(
string value)
219 this.overridePlainText = value;
224 #region privateMethods
230 internal XmlElement GetXmlElement()
232 XmlElement siElement = XmlElement.CreateElement(SI_TAG);
239 foreach (TextRun run
in Runs)
241 XmlElement rElement = siElement.AddChildElement(R_TAG);
245 XmlElement rPrElement = CreateRunPropertiesElement(run.
FontStyle);
246 rElement.AddChildElement(rPrElement);
249 XmlElement tElement = CreateTextElement(run.
Text);
250 rElement.AddChildElement(tElement);
255 XmlElement rPhElement = CreatePhoneticRunElement(phoneticRun);
256 siElement.AddChildElement(rPhElement);
262 siElement.AddChildElement(phoneticPrElement);
272 private void CheckWrapText(
string text)
274 if (text.Contains(
"\n"))
284 XmlElement IFormattableText.GetXmlElement()
286 return GetXmlElement();
295 private static XmlElement CreatePhoneticPropertiesElement(
PhoneticProperties properties)
297 XmlElement phoneticPrElement = XmlElement.CreateElement(PHONETIC_PR_TAG);
298 phoneticPrElement.AddAttribute(
"fontId", ParserUtils.ToString(properties.
FontId));
299 string typeValue = GetPhoneticTypeValue(properties.
Type);
300 phoneticPrElement.AddAttribute(
"type", typeValue);
301 string alignmentValue = GetPhoneticAlignmentValue(properties.
Alignment);
302 phoneticPrElement.AddAttribute(
"alignment", alignmentValue);
303 return phoneticPrElement;
312 private static XmlElement CreateRunPropertiesElement(Font fontStyle)
314 XmlElement rPrElement = XmlElement.CreateElement(RPR_TAG);
315 if (!
string.IsNullOrEmpty(fontStyle.Name))
317 rPrElement.AddChildElementWithAttribute(
"rFont",
"val", fontStyle.Name);
319 if (fontStyle.Charset != Font.CharsetValue.Default)
321 rPrElement.AddChildElementWithAttribute(
"charset",
"val", ParserUtils.ToString((
int)fontStyle.Charset));
323 if (fontStyle.Family != Font.DefaultFontFamily)
325 rPrElement.AddChildElementWithAttribute(
"family",
"val", ParserUtils.ToString((
int)fontStyle.Family));
329 rPrElement.AddChildElement(
"b");
331 if (fontStyle.Italic)
333 rPrElement.AddChildElement(
"i");
335 if (fontStyle.Strike)
337 rPrElement.AddChildElement(
"strike");
339 if (fontStyle.Outline)
341 rPrElement.AddChildElement(
"outline");
343 if (fontStyle.Shadow)
345 rPrElement.AddChildElement(
"shadow");
347 if (fontStyle.Condense)
349 rPrElement.AddChildElement(
"condense");
351 if (fontStyle.Extend)
353 rPrElement.AddChildElement(
"extend");
355 if (fontStyle.ColorValue !=
null && fontStyle.ColorValue.IsDefined)
357 XmlElement colorElement = CreateColorElement(fontStyle.ColorValue);
358 rPrElement.AddChildElement(colorElement);
360 if (fontStyle.Size != Font.DefaultFontSize)
362 rPrElement.AddChildElementWithAttribute(
"sz",
"val", ParserUtils.ToString(fontStyle.Size));
364 if (fontStyle.Underline != UnderlineValue.None && fontStyle.Underline != UnderlineValue.Single)
366 rPrElement.AddChildElementWithAttribute(
"u",
"val", Font.GetUnderlineName(fontStyle.Underline));
368 else if (fontStyle.Underline == UnderlineValue.Single)
370 rPrElement.AddChildElement(
"u");
372 if (fontStyle.VerticalAlign != Font.VerticalTextAlignValue.None)
374 string vertAlignValue = Font.GetVerticalTextAlignName(fontStyle.VerticalAlign);
375 rPrElement.AddChildElementWithAttribute(
"vertAlign",
"val", vertAlignValue);
377 if (fontStyle.Scheme != Font.SchemeValue.None)
379 if (fontStyle.Scheme == SchemeValue.Major)
381 rPrElement.AddChildElementWithAttribute(
"scheme",
"val",
"major");
383 else if (fontStyle.Scheme == SchemeValue.Minor)
384 { rPrElement.AddChildElementWithAttribute(
"scheme",
"val",
"minor"); }
395 private static XmlElement CreateTextElement(
string text)
397 string value = XmlUtils.SanitizeXmlValue(text);
398 value = ParserUtils.NormalizeNewLines(value);
400 if (
char.IsWhiteSpace(value, 0) ||
char.IsWhiteSpace(value, value.Length - 1))
402 element = XmlElement.CreateElementWithAttribute(
404 PRESERVE_ATTRIBUTE_NAME,
405 PRESERVE_ATTRIBUTE_VALUE,
407 PRESERVE_ATTRIBUTE_PREFIX);
411 element = XmlElement.CreateElement(T_TAG);
413 element.InnerValue = value;
422 private static XmlElement CreateColorElement(Color color)
424 XmlElement colorElement = XmlElement.CreateElement(
"color");
425 if (color.Value is AutoColor)
427 colorElement.AddAttribute(
"auto",
"1");
429 else if (color.Value is IndexedColor)
431 colorElement.AddAttribute(
"indexed", color.Value.StringValue);
433 else if (color.Value is SrgbColor)
435 colorElement.AddAttribute(
"rgb", color.Value.StringValue);
437 else if (color.Value is ThemeColor)
439 colorElement.AddAttribute(
"theme", color.Value.StringValue);
441 else if (color.Value is SystemColor)
443 colorElement.AddAttribute(
"system", color.Value.StringValue);
445 if (color.Tint.HasValue)
447 colorElement.AddAttribute(
"tint", ParserUtils.ToString(color.Tint.Value));
457 private static XmlElement CreatePhoneticRunElement(PhoneticRun phoneticRun)
459 XmlElement rPhElement = XmlElement.CreateElement(RPH_TAG);
460 rPhElement.AddAttribute(
"sb", ParserUtils.ToString(phoneticRun.
StartBase));
461 rPhElement.AddAttribute(
"eb", ParserUtils.ToString(phoneticRun.
EndBase));
462 XmlElement tElement = CreateTextElement(phoneticRun.
Text);
463 rPhElement.AddChildElement(tElement);
472 private static string GetPhoneticTypeValue(PhoneticRun.PhoneticType type)
476 case PhoneticRun.PhoneticType.HalfwidthKatakana:
477 return "halfwidthKatakana";
478 case PhoneticRun.PhoneticType.Hiragana:
480 case PhoneticRun.PhoneticType.NoConversion:
481 return "noConversion";
483 return "fullwidthKatakana";
492 private static string GetPhoneticAlignmentValue(PhoneticRun.PhoneticAlignment alignment)
496 case PhoneticRun.PhoneticAlignment.NoControl:
498 case PhoneticRun.PhoneticAlignment.Center:
500 case PhoneticRun.PhoneticAlignment.Distributed:
501 return "distributed";
514 if (!(obj is FormattedText text))
517 return runs.SequenceEqual(text.runs) &&
518 phoeticRuns.SequenceEqual(text.phoeticRuns) &&
520 overridePlainText == text.overridePlainText &&
521 EqualityComparer<PhoneticProperties>.Default.Equals(
PhoneticProperties, text.PhoneticProperties);
530 var hashCode = 703246462;
531 foreach (var run
in runs)
533 hashCode = hashCode * -1521134295 + (run?.GetHashCode() ?? 0);
535 foreach (var phoneticRun
in phoeticRuns)
537 hashCode = hashCode * -1521134295 + (phoneticRun?.GetHashCode() ?? 0);
539 hashCode = hashCode * -1521134295 +
WrapText.GetHashCode();
540 hashCode = hashCode * -1521134295 + (overridePlainText?.GetHashCode() ?? 0);