Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SmallCaps Rendering #39

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions Source/Demo/Common/Samples/02.Text.htm
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
<head>
<title>Text</title>
<link rel="Stylesheet" href="StyleSheet" />
<style type="text/css">
::selection {
color: #bb5555;
background-color: #aaddaa;
}
<style type="text/css">
::selection {
color: #bb5555;
background-color: #aaddaa;
}
</style>
</head>
<body>
Expand All @@ -24,6 +24,7 @@ <h2>Formatting
<span style="color: rgb(160, 80, 90)">Colors</span></li>
<li><span style="background-color: red">Back colors</span>, <span style="background-color: #8dd">Back colors</span>, <span style="background-color: rgb(160, 80, 90)">Back colors</span></li>
<li><span style="font-style: normal">Font style</span>, <span style="font-style: italic">Font style</span>, <span style="font-weight: bolder">Font style</span>, <span style="text-decoration: underline">Font style</span>, <span style="text-decoration: line-through">Font style</span></li>
<li><span style="font-variant: normal">Font variant</span>, <span style="font-variant: small-caps">Font variant (<span style="font-style: italic">Font style</span>, <span style="font-weight: bolder">Font style</span>, <span style="text-decoration: underline">Font style</span>, <span style="text-decoration: line-through">Font style</span>)</span></li>
</ul>
<p>
Lorem ipsum <span style="font-style: italic; text-decoration: underline">dolor sit amet</span>,
Expand Down
1 change: 1 addition & 0 deletions Source/Demo/Common/Samples/03.Tables.htm
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ <h2>
<li><span style="font-style: normal">Font style</span>, <span style="font-style: italic">
Font style</span>, <span style="font-weight: bolder">Font style</span>, <span style="text-decoration: underline">
Font style</span>, <span style="text-decoration: line-through">Font style</span></li>
<li><span style="font-variant: normal">Font variant</span>, <span style="font-variant: small-caps">Font variant</span></li>
</ul>
</td>
<td valign="middle">
Expand Down
14 changes: 7 additions & 7 deletions Source/Demo/Common/TestSamples/12.Text.htm
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
<head>
<title>Text</title>
<link rel="Stylesheet" href="StyleSheet" />
<style>
.right {
text-align: right;
border-width: 1px;
border-style: solid;
border-color: blue;
}
<style>
.right {
text-align: right;
border-width: 1px;
border-style: solid;
border-color: blue;
}
</style>
</head>
<body>
Expand Down
34 changes: 17 additions & 17 deletions Source/Demo/Common/TestSamples/22.RTL.htm
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
<html>
<body>
<div dir="rtl">
<div>שלום עולם, יש <b>ברבורים </b>בעגם הזה</div>
<br />
<div>שלום עולם, יש ברבורים בעגם הזה</div>
<div>שלום עולם, יש ברבורים בעגם הזה</div>
</div>
<hr />
<div dir="rtl">
<div>שלום עולם,<span>hello world</span> יש ברבורים בעגם הזה</div>
<br />
<div>שלום עולם, יש ברבורים בעגם הזה</div>
<div>שלום עולם, יש ברבורים בעגם הזה</div>
</div>
<hr />
</body>
<html>
<body>
<div dir="rtl">
<div>שלום עולם, יש <b>ברבורים </b>בעגם הזה</div>
<br />
<div>שלום עולם, יש ברבורים בעגם הזה</div>
<div>שלום עולם, יש ברבורים בעגם הזה</div>
</div>
<hr />
<div dir="rtl">
<div>שלום עולם,<span>hello world</span> יש ברבורים בעגם הזה</div>
<br />
<div>שלום עולם, יש ברבורים בעגם הזה</div>
<div>שלום עולם, יש ברבורים בעגם הזה</div>
</div>
<hr />
</body>
</html>
2 changes: 1 addition & 1 deletion Source/HtmlRenderer.PdfSharp/Adapters/FontAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public override double GetWhitespaceWidth(RGraphics graphics)
/// </summary>
/// <param name="height">the full height of the font</param>
/// <param name="underlineOffset">the vertical offset of the font underline location from the top of the font.</param>
internal void SetMetrics(int height, int underlineOffset)
internal void SetMetrics(int height, double underlineOffset)
{
_height = height;
_underlineOffset = underlineOffset;
Expand Down
37 changes: 36 additions & 1 deletion Source/HtmlRenderer.PdfSharp/Adapters/GraphicsAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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)
{
Expand Down
72 changes: 72 additions & 0 deletions Source/HtmlRenderer.WPF/Adapters/GraphicsAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
35 changes: 35 additions & 0 deletions Source/HtmlRenderer.WinForms/Adapters/GraphicsAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
47 changes: 47 additions & 0 deletions Source/HtmlRenderer/Adapters/RGraphics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,42 @@ public RRect GetClip()
/// <returns>the size of the string</returns>
public abstract RSize MeasureString(string str, RFont font);

/// <summary>
/// Measure the width and height of string <paramref name="str"/> when drawn on device context HDC
/// using the given fonts: <paramref name="regularFont"/> <paramref name="reducedFont"/> (small-caps variant).
/// </summary>
/// <param name="str">the string to measure</param>
/// <param name="regularFont">the font to measure string with (uppercase chars)</param>
/// <param name="reducedFont">the font to measure string with (smallcap chars)</param>
/// <returns>the size of the string</returns>
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;
}

/// <summary>
/// Measure the width of string under max width restriction calculating the number of characters that can fit and the width those characters take.<br/>
/// Not relevant for platforms that don't render HTML on UI element.
Expand All @@ -167,6 +203,17 @@ public RRect GetClip()
/// <param name="rtl">is to render the string right-to-left (true - RTL, false - LTR)</param>
public abstract void DrawString(String str, RFont font, RColor color, RPoint point, RSize size, bool rtl);

/// <summary>
/// Draw the given string using the given fonts and foreground color at given location (small-caps variant).
/// </summary>
/// <param name="str">the string to draw</param>
/// <param name="regularFont">the font to use to draw the string (uppercase chars)</param>
/// <param name="reducedFont">the reduced font to use to draw the string (small-cap chars)</param>
/// <param name="color">the text color to set</param>
/// <param name="point">the location to start string draw (top-left)</param>
/// <param name="rtl">is to render the string right-to-left (true - RTL, false - LTR)</param>
public abstract void DrawSmallCapString(string str, RFont regularFont, RFont reducedFont, RColor color, RPoint point, bool rtl);

/// <summary>
/// Draws a line connecting the two points specified by the coordinate pairs.
/// </summary>
Expand Down
17 changes: 16 additions & 1 deletion Source/HtmlRenderer/Core/Dom/CssBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
}
Expand All @@ -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)
{
Expand Down
Loading