Skip to content

Commit 55b1f82

Browse files
ChusoJAPeXpl0it3r
authored andcommitted
Small-Caps-Support
Add small-caps rendering. (Not checked in MONO. Not checked in Winforms using GDI+).
1 parent 0b3cd8c commit 55b1f82

File tree

12 files changed

+252
-30
lines changed

12 files changed

+252
-30
lines changed

Source/Demo/Common/Samples/02.Text.htm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ <h2>Formatting
2424
<span style="color: rgb(160, 80, 90)">Colors</span></li>
2525
<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>
2626
<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="font-style: italic;font-weight: bolder">Font style</span>, <span style="text-decoration: underline">Font style</span>, <span style="text-decoration: line-through">Font style</span></li>
27+
<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>
2728
</ul>
2829
<p>
2930
Lorem ipsum <span style="font-style: italic; text-decoration: underline">dolor sit amet</span>,

Source/Demo/Common/Samples/03.Tables.htm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ <h2>
7979
<li><span style="font-style: normal">Font style</span>, <span style="font-style: italic">
8080
Font style</span>, <span style="font-weight: bolder">Font style</span>, <span style="font-style: italic;font-weight: bolder">Font style</span>, <span style="text-decoration: underline">
8181
Font style</span>, <span style="text-decoration: line-through">Font style</span></li>
82+
<li><span style="font-variant: normal">Font variant</span>, <span style="font-variant: small-caps">Font variant</span></li>
8283
</ul>
8384
</td>
8485
<td valign="middle">

Source/Demo/Common/TestSamples/12.Text.htm

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
<head>
33
<title>Text</title>
44
<link rel="Stylesheet" href="StyleSheet" />
5-
<style>
6-
.right {
7-
text-align: right;
8-
border-width: 1px;
9-
border-style: solid;
10-
border-color: blue;
11-
}
5+
<style>
6+
.right {
7+
text-align: right;
8+
border-width: 1px;
9+
border-style: solid;
10+
border-color: blue;
11+
}
1212
</style>
1313
</head>
1414
<body>
Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
<html>
2-
<body>
3-
<div dir="rtl">
4-
<div>שלום עולם, יש <b>ברבורים </b>בעגם הזה</div>
5-
<br />
6-
<div>שלום עולם, יש ברבורים בעגם הזה</div>
7-
<div>שלום עולם, יש ברבורים בעגם הזה</div>
8-
</div>
9-
<hr />
10-
<div dir="rtl">
11-
<div>שלום עולם,<span>hello world</span> יש ברבורים בעגם הזה</div>
12-
<br />
13-
<div>שלום עולם, יש ברבורים בעגם הזה</div>
14-
<div>שלום עולם, יש ברבורים בעגם הזה</div>
15-
</div>
16-
<hr />
17-
</body>
1+
<html>
2+
<body>
3+
<div dir="rtl">
4+
<div>שלום עולם, יש <b>ברבורים </b>בעגם הזה</div>
5+
<br />
6+
<div>שלום עולם, יש ברבורים בעגם הזה</div>
7+
<div>שלום עולם, יש ברבורים בעגם הזה</div>
8+
</div>
9+
<hr />
10+
<div dir="rtl">
11+
<div>שלום עולם,<span>hello world</span> יש ברבורים בעגם הזה</div>
12+
<br />
13+
<div>שלום עולם, יש ברבורים בעגם הזה</div>
14+
<div>שלום עולם, יש ברבורים בעגם הזה</div>
15+
</div>
16+
<hr />
17+
</body>
1818
</html>

Source/HtmlRenderer.PdfSharp/Adapters/FontAdapter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public override double GetWhitespaceWidth(RGraphics graphics)
9696
/// </summary>
9797
/// <param name="height">the full height of the font</param>
9898
/// <param name="underlineOffset">the vertical offset of the font underline location from the top of the font.</param>
99-
internal void SetMetrics(int height, int underlineOffset)
99+
internal void SetMetrics(int height, double underlineOffset)
100100
{
101101
_height = height;
102102
_underlineOffset = underlineOffset;

Source/HtmlRenderer.PdfSharp/Adapters/GraphicsAdapter.cs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public override RSize MeasureString(string str, RFont font)
106106
{
107107
var height = realFont.Height;
108108
var descent = realFont.Size * realFont.FontFamily.GetCellDescent(realFont.Style) / realFont.FontFamily.GetEmHeight(realFont.Style);
109-
fontAdapter.SetMetrics(height, (int)Math.Round((height - descent + 1f)));
109+
fontAdapter.SetMetrics(height, height - descent + 1f);
110110
}
111111

112112
return Utils.Convert(size);
@@ -123,6 +123,41 @@ public override void DrawString(string str, RFont font, RColor color, RPoint poi
123123
var xBrush = ((BrushAdapter)_adapter.GetSolidBrush(color)).Brush;
124124
_g.DrawString(str, ((FontAdapter)font).Font, (XBrush)xBrush, point.X, point.Y, _stringFormat);
125125
}
126+
127+
public override void DrawSmallCapString(string str, RFont regularFont, RFont reducedFont, RColor color, RPoint point, bool rtl)
128+
{
129+
var xBrush = ((BrushAdapter)_adapter.GetSolidBrush(color)).Brush;
130+
double additionalOffsetForSmallCaps = regularFont.UnderlineOffset - reducedFont.UnderlineOffset;
131+
132+
int i = 0;
133+
int len = str.Length;
134+
while (i < len)
135+
{
136+
int j = i;
137+
while (j < len && !char.IsLower(str, j))
138+
j++;
139+
if (j != i)
140+
{
141+
_g.DrawString(str.Substring(i, j - i), ((FontAdapter)regularFont).Font, (XBrush)xBrush, point.X, point.Y, _stringFormat);
142+
point.X += MeasureString(str.Substring(i, j - i), regularFont).Width;
143+
144+
i = j;
145+
}
146+
147+
while (j < len && char.IsLower(str, j))
148+
j++;
149+
if (j != i)
150+
{
151+
point.Y += additionalOffsetForSmallCaps;
152+
153+
_g.DrawString(str.Substring(i, j - i).ToUpper(), ((FontAdapter)reducedFont).Font, (XBrush)xBrush, point.X, point.Y, _stringFormat);
154+
point.X += MeasureString(str.Substring(i, j - i).ToUpper(), reducedFont).Width;
155+
156+
point.Y -= additionalOffsetForSmallCaps;
157+
i = j;
158+
}
159+
}
160+
}
126161

127162
public override RBrush GetTextureBrush(RImage image, RRect dstRect, RPoint translateTransformLocation)
128163
{

Source/HtmlRenderer.WPF/Adapters/GraphicsAdapter.cs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,78 @@ public override void DrawString(string str, RFont font, RColor color, RPoint poi
221221
_g.DrawText(formattedText, Utils.ConvertRound(point));
222222
}
223223
}
224+
225+
public override void DrawSmallCapString(string str, RFont regularFont, RFont reducedFont, RColor color, RPoint point, bool rtl)
226+
{
227+
int i = 0;
228+
int len = str.Length;
229+
while (i < len)
230+
{
231+
int j = i;
232+
while (j < len && !char.IsLower(str, j))
233+
j++;
234+
if (j != i)
235+
{
236+
DrawSmallCapString(str.Substring(i, j - i), regularFont, color, point, regularFont.Size, rtl);
237+
point.X += MeasureString(str.Substring(i, j - i), regularFont).Width;
238+
239+
i = j;
240+
}
241+
242+
while (j < len && char.IsLower(str, j))
243+
j++;
244+
if (j != i)
245+
{
246+
DrawSmallCapString(str.Substring(i, j - i).ToUpper(), reducedFont, color, point, regularFont.Size, rtl);
247+
point.X += MeasureString(str.Substring(i, j - i).ToUpper(), reducedFont).Width;
248+
249+
i = j;
250+
}
251+
}
252+
}
253+
254+
private void DrawSmallCapString(string str, RFont reducedFont, RColor color, RPoint point, double regularFontSize, bool rtl)
255+
{
256+
var colorConv = ((BrushAdapter)_adapter.GetSolidBrush(color)).Brush;
257+
258+
bool glyphRendered = false;
259+
GlyphTypeface glyphTypeface = ((FontAdapter)reducedFont).GlyphTypeface;
260+
if (glyphTypeface != null)
261+
{
262+
double width = 0;
263+
ushort[] glyphs = new ushort[str.Length];
264+
double[] widths = new double[str.Length];
265+
266+
int i = 0;
267+
for (; i < str.Length; i++)
268+
{
269+
ushort glyph;
270+
if (!glyphTypeface.CharacterToGlyphMap.TryGetValue(str[i], out glyph))
271+
break;
272+
273+
glyphs[i] = glyph;
274+
width += glyphTypeface.AdvanceWidths[glyph];
275+
widths[i] = 96d / 72d * reducedFont.Size * glyphTypeface.AdvanceWidths[glyph];
276+
}
277+
278+
if (i >= str.Length)
279+
{
280+
point.Y += glyphTypeface.Baseline * regularFontSize * 96d / 72d; // Align vertically reduced (small-cap) chars to regular (uppercase) chars
281+
point.X += rtl ? 96d / 72d * reducedFont.Size * width : 0;
282+
283+
glyphRendered = true;
284+
var glyphRun = new GlyphRun(glyphTypeface, rtl ? 1 : 0, false, 96d / 72d * reducedFont.Size, glyphs, Utils.ConvertRound(point), widths, null, null, null, null, null, null);
285+
_g.DrawGlyphRun(colorConv, glyphRun);
286+
}
287+
}
288+
289+
if (!glyphRendered) // Untested...
290+
{
291+
var formattedText = new FormattedText(str, CultureInfo.CurrentCulture, rtl ? FlowDirection.RightToLeft : FlowDirection.LeftToRight, ((FontAdapter)reducedFont).Font, 96d / 72d * reducedFont.Size, colorConv);
292+
point.X += rtl ? formattedText.Width : 0;
293+
_g.DrawText(formattedText, Utils.ConvertRound(point));
294+
}
295+
}
224296

225297
public override RBrush GetTextureBrush(RImage image, RRect dstRect, RPoint translateTransformLocation)
226298
{

Source/HtmlRenderer.WinForms/Adapters/GraphicsAdapter.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,41 @@ public override void DrawString(string str, RFont font, RColor color, RPoint poi
263263
#endif
264264
}
265265
}
266+
267+
public override void DrawSmallCapString(string str, RFont regularFont, RFont reducedFont, RColor color, RPoint point, bool rtl)
268+
{
269+
double additionalOffsetForSmallCaps = regularFont.UnderlineOffset - reducedFont.UnderlineOffset;
270+
int i = 0;
271+
int len = str.Length;
272+
while (i < len)
273+
{
274+
int j = i;
275+
while (j < len && !char.IsLower(str, j))
276+
j++;
277+
if (j != i)
278+
{
279+
RSize normalSize = MeasureString(str.Substring(i, j - i), regularFont);
280+
DrawString(str.Substring(i, j - i), regularFont, color, point, new RSize(normalSize.Width, normalSize.Height), rtl);
281+
point.X += normalSize.Width;
282+
283+
i = j;
284+
}
285+
286+
while (j < len && char.IsLower(str, j))
287+
j++;
288+
if (j != i)
289+
{
290+
point.Y += additionalOffsetForSmallCaps;
291+
292+
RSize reducedSize = MeasureString(str.Substring(i, j - i).ToUpper(), reducedFont);
293+
DrawString(str.Substring(i, j - i).ToUpper(), reducedFont, color, point, new RSize(reducedSize.Width, reducedSize.Height), rtl);
294+
point.X += reducedSize.Width;
295+
296+
point.Y -= additionalOffsetForSmallCaps;
297+
i = j;
298+
}
299+
}
300+
}
266301

267302
public override RBrush GetTextureBrush(RImage image, RRect dstRect, RPoint translateTransformLocation)
268303
{

Source/HtmlRenderer/Adapters/RGraphics.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,42 @@ public void ResumeClipping()
176176
/// <returns>the size of the string</returns>
177177
public abstract RSize MeasureString(string str, RFont font);
178178

179+
/// <summary>
180+
/// Measure the width and height of string <paramref name="str"/> when drawn on device context HDC
181+
/// using the given fonts: <paramref name="regularFont"/> <paramref name="reducedFont"/> (small-caps variant).
182+
/// </summary>
183+
/// <param name="str">the string to measure</param>
184+
/// <param name="regularFont">the font to measure string with (uppercase chars)</param>
185+
/// <param name="reducedFont">the font to measure string with (smallcap chars)</param>
186+
/// <returns>the size of the string</returns>
187+
public RSize MeasureSmallCapString(string str, RFont regularFont, RFont reducedFont)
188+
{
189+
RSize size = RSize.Empty;
190+
size.Height = regularFont.Height;
191+
int i = 0;
192+
int len = str.Length;
193+
while (i < len)
194+
{
195+
int j = i;
196+
while (j < len && !char.IsLower(str, j))
197+
j++;
198+
if (j != i)
199+
{
200+
size.Width += MeasureString(str.Substring(i, j - i), regularFont).Width;
201+
i = j;
202+
}
203+
204+
while (j < len && char.IsLower(str, j))
205+
j++;
206+
if (j != i)
207+
{
208+
size.Width += MeasureString(str.Substring(i, j - i).ToUpper(), reducedFont).Width;
209+
i = j;
210+
}
211+
}
212+
return size;
213+
}
214+
179215
/// <summary>
180216
/// Measure the width of string under max width restriction calculating the number of characters that can fit and the width those characters take.<br/>
181217
/// Not relevant for platforms that don't render HTML on UI element.
@@ -198,6 +234,17 @@ public void ResumeClipping()
198234
/// <param name="rtl">is to render the string right-to-left (true - RTL, false - LTR)</param>
199235
public abstract void DrawString(String str, RFont font, RColor color, RPoint point, RSize size, bool rtl);
200236

237+
/// <summary>
238+
/// Draw the given string using the given fonts and foreground color at given location (small-caps variant).
239+
/// </summary>
240+
/// <param name="str">the string to draw</param>
241+
/// <param name="regularFont">the font to use to draw the string (uppercase chars)</param>
242+
/// <param name="reducedFont">the reduced font to use to draw the string (small-cap chars)</param>
243+
/// <param name="color">the text color to set</param>
244+
/// <param name="point">the location to start string draw (top-left)</param>
245+
/// <param name="rtl">is to render the string right-to-left (true - RTL, false - LTR)</param>
246+
public abstract void DrawSmallCapString(string str, RFont regularFont, RFont reducedFont, RColor color, RPoint point, bool rtl);
247+
201248
/// <summary>
202249
/// Draws a line connecting the two points specified by the coordinate pairs.
203250
/// </summary>

Source/HtmlRenderer/Core/Dom/CssBox.cs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,9 @@ internal virtual void MeasureWordsSize(RGraphics g)
723723
{
724724
foreach (var boxWord in Words)
725725
{
726+
if (FontVariant == CssConstants.SmallCaps)
727+
boxWord.Width = boxWord.Text != "\n" ? g.MeasureSmallCapString(boxWord.Text, ActualFont, ActualFontForSmallCaps).Width : 0;
728+
else
726729
boxWord.Width = boxWord.Text != "\n" ? g.MeasureString(boxWord.Text, ActualFont).Width : 0;
727730
boxWord.Height = ActualFont.Height;
728731
}
@@ -1388,21 +1391,30 @@ private void PaintWords(RGraphics g, RPoint offset)
13881391
if (HtmlContainer.SelectionForeColor != RColor.Empty && (word.SelectedStartOffset > 0 || word.SelectedEndIndexOffset > -1))
13891392
{
13901393
g.PushClipExclude(rect);
1391-
g.DrawString(word.Text, ActualFont, ActualColor, wordPoint, new RSize(word.Width, word.Height), isRtl);
1394+
if (FontVariant == CssConstants.SmallCaps)
1395+
g.DrawSmallCapString(word.Text, ActualFont, ActualFontForSmallCaps, ActualColor, wordPoint, isRtl);
1396+
else
1397+
g.DrawString(word.Text, ActualFont, ActualColor, wordPoint, new RSize(word.Width, word.Height), isRtl);
13921398
g.PopClip();
13931399
g.PushClip(rect);
13941400
g.DrawString(word.Text, ActualFont, GetSelectionForeBrush(), wordPoint, new RSize(word.Width, word.Height), isRtl);
13951401
g.PopClip();
13961402
}
13971403
else
13981404
{
1399-
g.DrawString(word.Text, ActualFont, GetSelectionForeBrush(), wordPoint, new RSize(word.Width, word.Height), isRtl);
1405+
if (FontVariant == CssConstants.SmallCaps)
1406+
g.DrawSmallCapString(word.Text, ActualFont, ActualFontForSmallCaps, GetSelectionForeBrush(), wordPoint, isRtl);
1407+
else
1408+
g.DrawString(word.Text, ActualFont, GetSelectionForeBrush(), wordPoint, new RSize(word.Width, word.Height), isRtl);
14001409
}
14011410
}
14021411
else
14031412
{
1404-
// g.DrawRectangle(HtmlContainer.Adapter.GetPen(RColor.Black), wordPoint.X, wordPoint.Y, word.Width - 1, word.Height - 1);
1405-
g.DrawString(word.Text, ActualFont, ActualColor, wordPoint, new RSize(word.Width, word.Height), isRtl);
1413+
if (FontVariant == CssConstants.SmallCaps)
1414+
g.DrawSmallCapString(word.Text, ActualFont, ActualFontForSmallCaps, ActualColor, wordPoint, isRtl);
1415+
else
1416+
//g.DrawRectangle(HtmlContainer.Adapter.GetPen(RColor.Black), wordPoint.X, wordPoint.Y, word.Width - 1, word.Height - 1);
1417+
g.DrawString(word.Text, ActualFont, ActualColor, wordPoint, new RSize(word.Width, word.Height), isRtl);
14061418
}
14071419
}
14081420
}

0 commit comments

Comments
 (0)