I'm creating Text using the FormattedText class - but how can I subscript oder superscript Text when using this class? I found solution on how to do this when using a TextBlock, but I'm using FormattedText and not the TextBlock ): Thanks for any hint!
FormattedText
can't do subscript/superscript - but TextFormatter
can.
TextFormatter
is a low level API, you need to write a lot of code to use it - but most of the code is just subclassing all the classes used to pass formatting parameters int TextFormatter
.
TextFormatter
takes a TextSource
object and produces multiple TextLine
objects (one for each line), the TextLine.Draw
method can then be used to draw the line to a drawing context.
The TextSource
class is abstract, you have to subclass it and override the GetTextRun
method that simply returns a TextRun
object that's in the character position provided.
TextRun
is also abstract - but it does have subclasses you can use - the interesting class is TextCharacters
that contains a string and formatting information.
The formatting information is in a TextRunProperties
object, unfortunately, this is another abstract class you have to subclass.
TextRunProperties
has a TypographyProperties
property of type TextRunTypographyProperties
.
TextRunTypographyProperties
is yet another abstract class you need to subclass.
And finally TextRunTypographyProperties
has the Variants
property you can use like the TextBlock example.
Here is the minimal code I could write to draw superscript text:
First, Our TextRunProperties and TextRunTypographyProperties that can return superscript font variant:
class CustomTextRunProperties : TextRunProperties
{
private bool _superscript;
public CustomTextRunProperties(bool superscript)
{
_superscript = superscript;
}
public override System.Windows.Media.Brush BackgroundBrush
{
get { return null; }
}
public override CultureInfo CultureInfo
{
get { return CultureInfo.CurrentCulture; }
}
public override double FontHintingEmSize
{
get { return 22; }
}
public override double FontRenderingEmSize
{
get { return 22; }
}
public override Brush ForegroundBrush
{
get { return Brushes.Black; }
}
public override System.Windows.TextDecorationCollection TextDecorations
{
get { return new System.Windows.TextDecorationCollection(); }
}
public override System.Windows.Media.TextEffectCollection TextEffects
{
get { return new TextEffectCollection(); }
}
public override System.Windows.Media.Typeface Typeface
{
get { return new Typeface("Calibri"); }
}
public override TextRunTypographyProperties TypographyProperties
{
get
{
return new CustomTextRunTypographyProperties(_superscript);
}
}
}
class CustomTextRunTypographyProperties : TextRunTypographyProperties
{
private bool _superscript;
public CustomTextRunTypographyProperties(bool superscript)
{
_superscript = superscript;
}
public override int AnnotationAlternates
{
get { return 0; }
}
public override bool CapitalSpacing
{
get { return false; }
}
public override System.Windows.FontCapitals Capitals
{
get { return FontCapitals.Normal; }
}
public override bool CaseSensitiveForms
{
get { return false; }
}
public override bool ContextualAlternates
{
get { return false; }
}
public override bool ContextualLigatures
{
get { return false; }
}
public override int ContextualSwashes
{
get { return 0; }
}
public override bool DiscretionaryLigatures
{
get { return false; }
}
public override bool EastAsianExpertForms
{
get { return false; }
}
public override System.Windows.FontEastAsianLanguage EastAsianLanguage
{
get { return FontEastAsianLanguage.Normal; }
}
public override System.Windows.FontEastAsianWidths EastAsianWidths
{
get { return FontEastAsianWidths.Normal; }
}
public override System.Windows.FontFraction Fraction
{
get { return FontFraction.Normal; }
}
public override bool HistoricalForms
{
get { return false; }
}
public override bool HistoricalLigatures
{
get { return false; }
}
public override bool Kerning
{
get { return true; }
}
public override bool MathematicalGreek
{
get { return false; }
}
public override System.Windows.FontNumeralAlignment NumeralAlignment
{
get { return FontNumeralAlignment.Normal; }
}
public override System.Windows.FontNumeralStyle NumeralStyle
{
get { return FontNumeralStyle.Normal; }
}
public override bool SlashedZero
{
get { return false; }
}
public override bool StandardLigatures
{
get { return false; }
}
public override int StandardSwashes
{
get { return 0; }
}
public override int StylisticAlternates
{
get { return 0; }
}
public override bool StylisticSet1
{
get { return false; }
}
public override bool StylisticSet10
{
get { return false; }
}
public override bool StylisticSet11
{
get { return false; }
}
public override bool StylisticSet12
{
get { return false; }
}
public override bool StylisticSet13
{
get { return false; }
}
public override bool StylisticSet14
{
get { return false; }
}
public override bool StylisticSet15
{
get { return false; }
}
public override bool StylisticSet16
{
get { return false; }
}
public override bool StylisticSet17
{
get { return false; }
}
public override bool StylisticSet18
{
get { return false; }
}
public override bool StylisticSet19
{
get { return false; }
}
public override bool StylisticSet2
{
get { return false; }
}
public override bool StylisticSet20
{
get { return false; }
}
public override bool StylisticSet3
{
get { return false; }
}
public override bool StylisticSet4
{
get { return false; }
}
public override bool StylisticSet5
{
get { return false; }
}
public override bool StylisticSet6
{
get { return false; }
}
public override bool StylisticSet7
{
get { return false; }
}
public override bool StylisticSet8
{
get { return false; }
}
public override bool StylisticSet9
{
get { return false; }
}
public override FontVariants Variants
{
get { return _superscript ? FontVariants.Superscript: FontVariants.Normal; }
}
}
And a similar class for paragraph formatting (takes from MSDN TextFormatter sample):
class GenericTextParagraphProperties : TextParagraphProperties
{
public GenericTextParagraphProperties(
FlowDirection flowDirection,
TextAlignment textAlignment,
bool firstLineInParagraph,
bool alwaysCollapsible,
TextRunProperties defaultTextRunProperties,
TextWrapping textWrap,
double lineHeight,
double indent)
{
_flowDirection = flowDirection;
_textAlignment = textAlignment;
_firstLineInParagraph = firstLineInParagraph;
_alwaysCollapsible = alwaysCollapsible;
_defaultTextRunProperties = defaultTextRunProperties;
_textWrap = textWrap;
_lineHeight = lineHeight;
_indent = indent;
}
public override FlowDirection FlowDirection
{
get { return _flowDirection; }
}
public override TextAlignment TextAlignment
{
get { return _textAlignment; }
}
public override bool FirstLineInParagraph
{
get { return _firstLineInParagraph; }
}
public override bool AlwaysCollapsible
{
get { return _alwaysCollapsible; }
}
public override TextRunProperties DefaultTextRunProperties
{
get { return _defaultTextRunProperties; }
}
public override TextWrapping TextWrapping
{
get { return _textWrap; }
}
public override double LineHeight
{
get { return _lineHeight; }
}
public override double Indent
{
get { return _indent; }
}
public override TextMarkerProperties TextMarkerProperties
{
get { return null; }
}
public override double ParagraphIndent
{
get { return _paragraphIndent; }
}
private FlowDirection _flowDirection;
private TextAlignment _textAlignment;
private bool _firstLineInParagraph;
private bool _alwaysCollapsible;
private TextRunProperties _defaultTextRunProperties;
private TextWrapping _textWrap;
private double _indent;
private double _paragraphIndent;
private double _lineHeight;
}
Now TextSource implementation:
public class CustomTextSourceRun
{
public string Text;
public bool IsSuperscript;
public bool IsEndParagraph;
public int Length { get { return IsEndParagraph ? 1 : Text.Length; } }
}
class CustomTextSource : TextSource
{
public List<CustomTextSourceRun> Runs = new List<CustomTextSourceRun>();
public override TextRun GetTextRun(int textSourceCharacterIndex)
{
int pos = 0;
foreach (var currentRun in Runs)
{
if (textSourceCharacterIndex < pos + currentRun.Length)
{
if (currentRun.IsEndParagraph)
{
return new TextEndOfParagraph(1);
}
var props =
new CustomTextRunProperties(currentRun.IsSuperscript);
return new TextCharacters(
currentRun.Text,
textSourceCharacterIndex - pos,
currentRun.Length - (textSourceCharacterIndex - pos),
props);
}
pos += currentRun.Length;
}
// Return an end-of-paragraph if no more text source.
return new TextEndOfParagraph(1);
}
public override TextSpan<CultureSpecificCharacterBufferRange> GetPrecedingText(int textSourceCharacterIndexLimit)
{
throw new Exception("The method or operation is not implemented.");
}
public override int GetTextEffectCharacterIndexFromTextSourceCharacterIndex(int textSourceCharacterIndex)
{
throw new Exception("The method or operation is not implemented.");
}
public int Length
{
get
{
int r = 0;
foreach (var currentRun in Runs)
{
r += currentRun.Length;
}
return r;
}
}
}
And all that's left to do is initialize out CustomTextSource and draw the text:
var textStore = new CustomTextSource();
textStore.Runs.Add(new CustomTextSourceRun() { Text = "3" });
textStore.Runs.Add(new CustomTextSourceRun() { Text = "rd", IsSuperscript = true });
textStore.Runs.Add(new CustomTextSourceRun() { IsEndParagraph = true });
textStore.Runs.Add(new CustomTextSourceRun() { Text = "4" });
textStore.Runs.Add(new CustomTextSourceRun() { Text = "th", IsSuperscript = true });
int textStorePosition = 0;
System.Windows.Point linePosition = new System.Windows.Point(0, 0);
textDest = new DrawingGroup();
DrawingContext dc = textDest.Open();
TextFormatter formatter = TextFormatter.Create();
while (textStorePosition < textStore.Length)
{
using (TextLine myTextLine = formatter.FormatLine(
textStore,
textStorePosition,
96*6,
new GenericTextParagraphProperties(FlowDirection.LeftToRight,
TextAlignment.Left,true,false, new CustomTextRunProperties(false), TextWrapping.Wrap,
30,0), null))
{
myTextLine.Draw(dc, linePosition, InvertAxes.None);
textStorePosition += myTextLine.Length;
linePosition.Y += myTextLine.Height;
}
}
dc.Close();
And that's it - we have superscript text in a drawing context.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With