diff --git a/Source/Demo/Common/Samples/02.Text.htm b/Source/Demo/Common/Samples/02.Text.htm
index b7aee3fa9..430946f10 100644
--- a/Source/Demo/Common/Samples/02.Text.htm
+++ b/Source/Demo/Common/Samples/02.Text.htm
@@ -2,11 +2,11 @@
Text
-
@@ -24,6 +24,7 @@ Formatting
Colors
Back colors, Back colors, Back colors
Font style, Font style, Font style, Font style, Font style
+ Font variant, Font variant (Font style, Font style, Font style, Font style)
Lorem ipsum dolor sit amet,
diff --git a/Source/Demo/Common/Samples/03.Tables.htm b/Source/Demo/Common/Samples/03.Tables.htm
index 7f8a07eb7..f6c89fdaa 100644
--- a/Source/Demo/Common/Samples/03.Tables.htm
+++ b/Source/Demo/Common/Samples/03.Tables.htm
@@ -79,6 +79,7 @@
Font style,
Font style, Font style,
Font style, Font style
+ Font variant, Font variant
diff --git a/Source/Demo/Common/TestSamples/12.Text.htm b/Source/Demo/Common/TestSamples/12.Text.htm
index c53a43609..833196bae 100644
--- a/Source/Demo/Common/TestSamples/12.Text.htm
+++ b/Source/Demo/Common/TestSamples/12.Text.htm
@@ -2,13 +2,13 @@
Text
-
diff --git a/Source/Demo/Common/TestSamples/22.RTL.htm b/Source/Demo/Common/TestSamples/22.RTL.htm
index 35bf9bf6c..a24d4f4e7 100644
--- a/Source/Demo/Common/TestSamples/22.RTL.htm
+++ b/Source/Demo/Common/TestSamples/22.RTL.htm
@@ -1,18 +1,18 @@
-
-
-
- שלום עולם, יש ברבורים בעגם הזה
-
- שלום עולם, יש ברבורים בעגם הזה
- שלום עולם, יש ברבורים בעגם הזה
-
-
-
- שלום עולם,hello world יש ברבורים בעגם הזה
-
- שלום עולם, יש ברבורים בעגם הזה
- שלום עולם, יש ברבורים בעגם הזה
-
-
-
+
+
+
+ שלום עולם, יש ברבורים בעגם הזה
+
+ שלום עולם, יש ברבורים בעגם הזה
+ שלום עולם, יש ברבורים בעגם הזה
+
+
+
+ שלום עולם,hello world יש ברבורים בעגם הזה
+
+ שלום עולם, יש ברבורים בעגם הזה
+ שלום עולם, יש ברבורים בעגם הזה
+
+
+
\ No newline at end of file
diff --git a/Source/HtmlRenderer.PdfSharp/Adapters/FontAdapter.cs b/Source/HtmlRenderer.PdfSharp/Adapters/FontAdapter.cs
index 07e7c40a9..b121789bb 100644
--- a/Source/HtmlRenderer.PdfSharp/Adapters/FontAdapter.cs
+++ b/Source/HtmlRenderer.PdfSharp/Adapters/FontAdapter.cs
@@ -96,7 +96,7 @@ public override double GetWhitespaceWidth(RGraphics graphics)
///
/// the full height of the font
/// the vertical offset of the font underline location from the top of the font.
- internal void SetMetrics(int height, int underlineOffset)
+ internal void SetMetrics(int height, double underlineOffset)
{
_height = height;
_underlineOffset = underlineOffset;
diff --git a/Source/HtmlRenderer.PdfSharp/Adapters/GraphicsAdapter.cs b/Source/HtmlRenderer.PdfSharp/Adapters/GraphicsAdapter.cs
index 9af8a9790..e28af6261 100644
--- a/Source/HtmlRenderer.PdfSharp/Adapters/GraphicsAdapter.cs
+++ b/Source/HtmlRenderer.PdfSharp/Adapters/GraphicsAdapter.cs
@@ -107,7 +107,7 @@ public override RSize MeasureString(string str, RFont font)
{
var height = realFont.Height;
var descent = realFont.Size * realFont.FontFamily.GetCellDescent(realFont.Style) / realFont.FontFamily.GetEmHeight(realFont.Style);
- fontAdapter.SetMetrics(height, (int)Math.Round((height - descent + 1f)));
+ fontAdapter.SetMetrics(height, height - descent + 1f);
}
return Utils.Convert(size);
@@ -124,6 +124,41 @@ public override void DrawString(string str, RFont font, RColor color, RPoint poi
var xBrush = ((BrushAdapter)_adapter.GetSolidBrush(color)).Brush;
_g.DrawString(str, ((FontAdapter)font).Font, (XBrush)xBrush, point.X, point.Y, _stringFormat);
}
+
+ public override void DrawSmallCapString(string str, RFont regularFont, RFont reducedFont, RColor color, RPoint point, bool rtl)
+ {
+ var xBrush = ((BrushAdapter)_adapter.GetSolidBrush(color)).Brush;
+ double additionalOffsetForSmallCaps = regularFont.UnderlineOffset - reducedFont.UnderlineOffset;
+
+ int i = 0;
+ int len = str.Length;
+ while (i < len)
+ {
+ int j = i;
+ while (j < len && !char.IsLower(str, j))
+ j++;
+ if (j != i)
+ {
+ _g.DrawString(str.Substring(i, j - i), ((FontAdapter)regularFont).Font, (XBrush)xBrush, point.X, point.Y, _stringFormat);
+ point.X += MeasureString(str.Substring(i, j - i), regularFont).Width;
+
+ i = j;
+ }
+
+ while (j < len && char.IsLower(str, j))
+ j++;
+ if (j != i)
+ {
+ point.Y += additionalOffsetForSmallCaps;
+
+ _g.DrawString(str.Substring(i, j - i).ToUpper(), ((FontAdapter)reducedFont).Font, (XBrush)xBrush, point.X, point.Y, _stringFormat);
+ point.X += MeasureString(str.Substring(i, j - i).ToUpper(), reducedFont).Width;
+
+ point.Y -= additionalOffsetForSmallCaps;
+ i = j;
+ }
+ }
+ }
public override RBrush GetTextureBrush(RImage image, RRect dstRect, RPoint translateTransformLocation)
{
diff --git a/Source/HtmlRenderer.WPF/Adapters/GraphicsAdapter.cs b/Source/HtmlRenderer.WPF/Adapters/GraphicsAdapter.cs
index d61bef4ca..5d24c3b55 100644
--- a/Source/HtmlRenderer.WPF/Adapters/GraphicsAdapter.cs
+++ b/Source/HtmlRenderer.WPF/Adapters/GraphicsAdapter.cs
@@ -212,6 +212,78 @@ public override void DrawString(string str, RFont font, RColor color, RPoint poi
_g.DrawText(formattedText, Utils.ConvertRound(point));
}
}
+
+ public override void DrawSmallCapString(string str, RFont regularFont, RFont reducedFont, RColor color, RPoint point, bool rtl)
+ {
+ int i = 0;
+ int len = str.Length;
+ while (i < len)
+ {
+ int j = i;
+ while (j < len && !char.IsLower(str, j))
+ j++;
+ if (j != i)
+ {
+ DrawSmallCapString(str.Substring(i, j - i), regularFont, color, point, regularFont.Size, rtl);
+ point.X += MeasureString(str.Substring(i, j - i), regularFont).Width;
+
+ i = j;
+ }
+
+ while (j < len && char.IsLower(str, j))
+ j++;
+ if (j != i)
+ {
+ DrawSmallCapString(str.Substring(i, j - i).ToUpper(), reducedFont, color, point, regularFont.Size, rtl);
+ point.X += MeasureString(str.Substring(i, j - i).ToUpper(), reducedFont).Width;
+
+ i = j;
+ }
+ }
+ }
+
+ private void DrawSmallCapString(string str, RFont reducedFont, RColor color, RPoint point, double regularFontSize, bool rtl)
+ {
+ var colorConv = ((BrushAdapter)_adapter.GetSolidBrush(color)).Brush;
+
+ bool glyphRendered = false;
+ GlyphTypeface glyphTypeface = ((FontAdapter)reducedFont).GlyphTypeface;
+ if (glyphTypeface != null)
+ {
+ double width = 0;
+ ushort[] glyphs = new ushort[str.Length];
+ double[] widths = new double[str.Length];
+
+ int i = 0;
+ for (; i < str.Length; i++)
+ {
+ ushort glyph;
+ if (!glyphTypeface.CharacterToGlyphMap.TryGetValue(str[i], out glyph))
+ break;
+
+ glyphs[i] = glyph;
+ width += glyphTypeface.AdvanceWidths[glyph];
+ widths[i] = 96d / 72d * reducedFont.Size * glyphTypeface.AdvanceWidths[glyph];
+ }
+
+ if (i >= str.Length)
+ {
+ point.Y += glyphTypeface.Baseline * regularFontSize * 96d / 72d; // Align vertically reduced (small-cap) chars to regular (uppercase) chars
+ point.X += rtl ? 96d / 72d * reducedFont.Size * width : 0;
+
+ glyphRendered = true;
+ var glyphRun = new GlyphRun(glyphTypeface, rtl ? 1 : 0, false, 96d / 72d * reducedFont.Size, glyphs, Utils.ConvertRound(point), widths, null, null, null, null, null, null);
+ _g.DrawGlyphRun(colorConv, glyphRun);
+ }
+ }
+
+ if (!glyphRendered) // Untested...
+ {
+ var formattedText = new FormattedText(str, CultureInfo.CurrentCulture, rtl ? FlowDirection.RightToLeft : FlowDirection.LeftToRight, ((FontAdapter)reducedFont).Font, 96d / 72d * reducedFont.Size, colorConv);
+ point.X += rtl ? formattedText.Width : 0;
+ _g.DrawText(formattedText, Utils.ConvertRound(point));
+ }
+ }
public override RBrush GetTextureBrush(RImage image, RRect dstRect, RPoint translateTransformLocation)
{
diff --git a/Source/HtmlRenderer.WinForms/Adapters/GraphicsAdapter.cs b/Source/HtmlRenderer.WinForms/Adapters/GraphicsAdapter.cs
index 523870c2b..b9e5fec1b 100644
--- a/Source/HtmlRenderer.WinForms/Adapters/GraphicsAdapter.cs
+++ b/Source/HtmlRenderer.WinForms/Adapters/GraphicsAdapter.cs
@@ -263,6 +263,41 @@ public override void DrawString(string str, RFont font, RColor color, RPoint poi
#endif
}
}
+
+ public override void DrawSmallCapString(string str, RFont regularFont, RFont reducedFont, RColor color, RPoint point, bool rtl)
+ {
+ double additionalOffsetForSmallCaps = regularFont.UnderlineOffset - reducedFont.UnderlineOffset;
+ int i = 0;
+ int len = str.Length;
+ while (i < len)
+ {
+ int j = i;
+ while (j < len && !char.IsLower(str, j))
+ j++;
+ if (j != i)
+ {
+ RSize normalSize = MeasureString(str.Substring(i, j - i), regularFont);
+ DrawString(str.Substring(i, j - i), regularFont, color, point, new RSize(normalSize.Width, normalSize.Height), rtl);
+ point.X += normalSize.Width;
+
+ i = j;
+ }
+
+ while (j < len && char.IsLower(str, j))
+ j++;
+ if (j != i)
+ {
+ point.Y += additionalOffsetForSmallCaps;
+
+ RSize reducedSize = MeasureString(str.Substring(i, j - i).ToUpper(), reducedFont);
+ DrawString(str.Substring(i, j - i).ToUpper(), reducedFont, color, point, new RSize(reducedSize.Width, reducedSize.Height), rtl);
+ point.X += reducedSize.Width;
+
+ point.Y -= additionalOffsetForSmallCaps;
+ i = j;
+ }
+ }
+ }
public override RBrush GetTextureBrush(RImage image, RRect dstRect, RPoint translateTransformLocation)
{
diff --git a/Source/HtmlRenderer/Adapters/RGraphics.cs b/Source/HtmlRenderer/Adapters/RGraphics.cs
index 040d14837..5c97b25ef 100644
--- a/Source/HtmlRenderer/Adapters/RGraphics.cs
+++ b/Source/HtmlRenderer/Adapters/RGraphics.cs
@@ -145,6 +145,42 @@ public RRect GetClip()
/// the size of the string
public abstract RSize MeasureString(string str, RFont font);
+ ///
+ /// Measure the width and height of string when drawn on device context HDC
+ /// using the given fonts: (small-caps variant).
+ ///
+ /// the string to measure
+ /// the font to measure string with (uppercase chars)
+ /// the font to measure string with (smallcap chars)
+ /// the size of the string
+ public RSize MeasureSmallCapString(string str, RFont regularFont, RFont reducedFont)
+ {
+ RSize size = RSize.Empty;
+ size.Height = regularFont.Height;
+ int i = 0;
+ int len = str.Length;
+ while (i < len)
+ {
+ int j = i;
+ while (j < len && !char.IsLower(str, j))
+ j++;
+ if (j != i)
+ {
+ size.Width += MeasureString(str.Substring(i, j - i), regularFont).Width;
+ i = j;
+ }
+
+ while (j < len && char.IsLower(str, j))
+ j++;
+ if (j != i)
+ {
+ size.Width += MeasureString(str.Substring(i, j - i).ToUpper(), reducedFont).Width;
+ i = j;
+ }
+ }
+ return size;
+ }
+
///
/// Measure the width of string under max width restriction calculating the number of characters that can fit and the width those characters take.
/// Not relevant for platforms that don't render HTML on UI element.
@@ -167,6 +203,17 @@ public RRect GetClip()
/// is to render the string right-to-left (true - RTL, false - LTR)
public abstract void DrawString(String str, RFont font, RColor color, RPoint point, RSize size, bool rtl);
+ ///
+ /// Draw the given string using the given fonts and foreground color at given location (small-caps variant).
+ ///
+ /// the string to draw
+ /// the font to use to draw the string (uppercase chars)
+ /// the reduced font to use to draw the string (small-cap chars)
+ /// the text color to set
+ /// the location to start string draw (top-left)
+ /// is to render the string right-to-left (true - RTL, false - LTR)
+ public abstract void DrawSmallCapString(string str, RFont regularFont, RFont reducedFont, RColor color, RPoint point, bool rtl);
+
///
/// Draws a line connecting the two points specified by the coordinate pairs.
///
diff --git a/Source/HtmlRenderer/Core/Dom/CssBox.cs b/Source/HtmlRenderer/Core/Dom/CssBox.cs
index edd9d6f0a..44a7ac95f 100644
--- a/Source/HtmlRenderer/Core/Dom/CssBox.cs
+++ b/Source/HtmlRenderer/Core/Dom/CssBox.cs
@@ -661,6 +661,9 @@ internal virtual void MeasureWordsSize(RGraphics g)
{
foreach (var boxWord in Words)
{
+ if (FontVariant == CssConstants.SmallCaps)
+ boxWord.Width = boxWord.Text != "\n" ? g.MeasureSmallCapString(boxWord.Text, ActualFont, ActualFontForSmallCaps).Width : 0;
+ else
boxWord.Width = boxWord.Text != "\n" ? g.MeasureString(boxWord.Text, ActualFont).Width : 0;
boxWord.Height = ActualFont.Height;
}
@@ -1271,19 +1274,31 @@ private void PaintWords(RGraphics g, RPoint offset)
if (HtmlContainer.SelectionForeColor != RColor.Empty && (word.SelectedStartOffset > 0 || word.SelectedEndIndexOffset > -1))
{
g.PushClipExclude(rect);
+ if (FontVariant == CssConstants.SmallCaps)
+ g.DrawSmallCapString(word.Text, ActualFont, ActualFontForSmallCaps, ActualColor, wordPoint, isRtl);
+ else
g.DrawString(word.Text, ActualFont, ActualColor, wordPoint, new RSize(word.Width, word.Height), isRtl);
g.PopClip();
g.PushClip(rect);
+ if (FontVariant == CssConstants.SmallCaps)
+ g.DrawSmallCapString(word.Text, ActualFont, ActualFontForSmallCaps, GetSelectionForeBrush(), wordPoint, isRtl);
+ else
g.DrawString(word.Text, ActualFont, GetSelectionForeBrush(), wordPoint, new RSize(word.Width, word.Height), isRtl);
g.PopClip();
}
else
{
+ if (FontVariant == CssConstants.SmallCaps)
+ g.DrawSmallCapString(word.Text, ActualFont, ActualFontForSmallCaps, GetSelectionForeBrush(), wordPoint, isRtl);
+ else
g.DrawString(word.Text, ActualFont, GetSelectionForeBrush(), wordPoint, new RSize(word.Width, word.Height), isRtl);
}
}
else
{
+ if (FontVariant == CssConstants.SmallCaps)
+ g.DrawSmallCapString(word.Text, ActualFont, ActualFontForSmallCaps, ActualColor, wordPoint, isRtl);
+ else
// g.DrawRectangle(HtmlContainer.Adapter.GetPen(RColor.Black), wordPoint.X, wordPoint.Y, word.Width - 1, word.Height - 1);
g.DrawString(word.Text, ActualFont, ActualColor, wordPoint, new RSize(word.Width, word.Height), isRtl);
}
@@ -1307,7 +1322,7 @@ protected void PaintDecoration(RGraphics g, RRect rectangle, bool isFirst, bool
double y = 0f;
if (TextDecoration == CssConstants.Underline)
{
- y = Math.Round(rectangle.Top + ActualFont.UnderlineOffset);
+ y = rectangle.Top + ActualFont.UnderlineOffset;
}
else if (TextDecoration == CssConstants.LineThrough)
{
diff --git a/Source/HtmlRenderer/Core/Dom/CssBoxProperties.cs b/Source/HtmlRenderer/Core/Dom/CssBoxProperties.cs
index 3acc6ed41..58cf846b6 100644
--- a/Source/HtmlRenderer/Core/Dom/CssBoxProperties.cs
+++ b/Source/HtmlRenderer/Core/Dom/CssBoxProperties.cs
@@ -150,6 +150,7 @@ internal abstract class CssBoxProperties
private RColor _actualBorderRightColor = RColor.Empty;
private RColor _actualBackgroundColor = RColor.Empty;
private RFont _actualFont;
+ private RFont _actualFontForSmallCaps;
#endregion
@@ -1283,11 +1284,25 @@ public RFont ActualFont
}
_actualFont = GetCachedFont(FontFamily, fsize, st);
+
+ if (FontVariant == CssConstants.SmallCaps)
+ {
+ double fsizeForSmallCaps = CssValueParser.ParseLength(CssConstants.FakeSmallCapReduction_em, fsize, fsize, null, true, true);
+ _actualFontForSmallCaps = GetCachedFont(FontFamily, fsizeForSmallCaps, st);
+ }
}
return _actualFont;
}
}
+ ///
+ /// Gets the font that should be actually used to paint small-uppercase chars in the text of the box (small-caps variant)
+ ///
+ public RFont ActualFontForSmallCaps
+ {
+ get { return _actualFontForSmallCaps; }
+ }
+
protected abstract RFont GetCachedFont(string fontFamily, double fsize, RFontStyle st);
///
@@ -1434,6 +1449,8 @@ protected void MeasureWordSpacing(RGraphics g)
string len = RegexParserUtils.Search(RegexParserUtils.CssLength, WordSpacing);
_actualWordSpacing += CssValueParser.ParseLength(len, 1, this);
}
+ if (FontVariant == CssConstants.SmallCaps)
+ ActualFontForSmallCaps.GetWhitespaceWidth(g); // Set metrics of the font
}
}
diff --git a/Source/HtmlRenderer/Core/Utils/CssConstants.cs b/Source/HtmlRenderer/Core/Utils/CssConstants.cs
index 352406244..d28d8cb9b 100644
--- a/Source/HtmlRenderer/Core/Utils/CssConstants.cs
+++ b/Source/HtmlRenderer/Core/Utils/CssConstants.cs
@@ -80,6 +80,8 @@ internal static class CssConstants
public const string Show = "show";
public const string Small = "small";
public const string Smaller = "smaller";
+ public const string SmallCaps = "small-caps";
+ public const string FakeSmallCapReduction_em = ".75em";
public const string Solid = "solid";
public const string Sub = "sub";
public const string Super = "super";
|