Skip to content

Commit 2001570

Browse files
jhaygood86eXpl0it3r
authored andcommitted
Fix font-family quote/list resolution and list-style-type: hebrew
- CssUtils/CssParser: the "font-family" CSS property was assigned as a raw, unresolved string, so a quoted or comma-separated value (e.g. "'Arial'" or "Verdana, sans-serif") never matched any real installed font by name and silently fell back to a generic default for every element that used one. Route it through the existing ParseFontFamily resolver (already used correctly for the legacy <font face="..."> attribute, just never wired up for the CSS property) instead of assigning the literal declared text. Also fixes ParseFontFamily's not-found fallback, which returned the literal string "inherit" as a sentinel - confusable with the real CSS keyword downstream - to CssConstants.DefaultFont instead. - CssEngine (Keywords/ListStyle/Map): list-style-type: hebrew parsed as an unrecognized keyword and silently fell back to decimal numbering, even though the Hebrew-numeral conversion table already existed and worked. Added the missing keyword/enum/map entries so the value reaches that existing conversion code. Same underlying gap and fix as PeachPDF's 1f340a55. - CommonUtils: the hebrew and armenian numbering tables were only 3 rows deep, silently dropping the thousands digit for any value >= 1000; added the missing thousands row to both, plus Hebrew's CSS Counter Styles Level 3 15/16 override (ט"ו/ט"ז instead of the naive combination, which would otherwise resemble the Tetragrammaton) - also sourced from PeachPDF's 1f340a55. Integration test suite: 25/29 -> 26/29 passing.
1 parent 5b414f5 commit 2001570

6 files changed

Lines changed: 56 additions & 8 deletions

File tree

‎Source/HtmlRenderer/Core/CssEngine/Enumerations/Keywords.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ internal static class Keywords
189189
public static readonly string UpperLatin = "upper-latin";
190190
public static readonly string Armenian = "armenian";
191191
public static readonly string Georgian = "georgian";
192+
public static readonly string Hebrew = "hebrew";
192193
public static readonly string LowerAlpha = "lower-alpha";
193194
public static readonly string UpperAlpha = "upper-alpha";
194195
public static readonly string XxSmall = "xx-small";

‎Source/HtmlRenderer/Core/CssEngine/Enumerations/ListStyle.cs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ internal enum ListStyle : byte
1414
LowerLatin,
1515
UpperLatin,
1616
Armenian,
17-
Georgian
17+
Georgian,
18+
Hebrew
1819
}
1920
}

‎Source/HtmlRenderer/Core/CssEngine/Model/Map.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ internal static class Map
178178
{Keywords.UpperLatin, ListStyle.UpperLatin},
179179
{Keywords.Armenian, ListStyle.Armenian},
180180
{Keywords.Georgian, ListStyle.Georgian},
181+
{Keywords.Hebrew, ListStyle.Hebrew},
181182
{Keywords.LowerAlpha, ListStyle.LowerLatin},
182183
{Keywords.UpperAlpha, ListStyle.UpperLatin},
183184
{Keywords.None, ListStyle.None}

‎Source/HtmlRenderer/Core/Parse/CssParser.cs‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,11 @@ public CssImage ParseBackgroundImage(string value)
166166
}
167167

168168
/// <summary>
169-
/// Parse a complex font family css property to check if it contains multiple fonts and if the font exists.<br/>
170-
/// returns the font family name to use or 'inherit' if failed.
169+
/// Parse a "font-family" value - possibly a comma-separated fallback list, each candidate
170+
/// possibly quoted (e.g. <c>'Helvetica Neue', Arial, sans-serif</c>) - into the first
171+
/// candidate that's actually installed/registered, or <see cref="CssConstants.DefaultFont"/>
172+
/// if none of them are. Previously returned the literal string "inherit" as a not-found
173+
/// sentinel, which was confusable with the real CSS <c>inherit</c> keyword downstream.
171174
/// </summary>
172175
/// <param name="value">the font-family value to parse</param>
173176
/// <returns>parsed font-family value</returns>
@@ -196,7 +199,7 @@ public string ParseFontFamily(string value)
196199
start = end + 1;
197200
}
198201

199-
return CssConstants.Inherit;
202+
return CssConstants.DefaultFont;
200203
}
201204

202205
#region Private methods

‎Source/HtmlRenderer/Core/Utils/CommonUtils.cs‎

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,19 @@ internal static class CommonUtils
4747
}
4848
};
4949

50+
/// <summary>
51+
/// Table to convert numbers into hebrew digits - CSS Counter Styles Level 3 §6.1's "hebrew"
52+
/// <c>@counter-style</c>. Row 3 (thousands) is each units-row letter with a geresh (U+05F3)
53+
/// appended, per the spec's additive-symbols list (e.g. 1000 is the same letter as 1 plus a
54+
/// geresh) - a 3-row table silently drops the thousands digit entirely for any value >= 1000.
55+
/// 15 and 16 need a further, non-tabular override - see <see cref="ConvertToAlphaNumber"/>.
56+
/// </summary>
5057
private static readonly string[,] _hebrewDigitsTable =
5158
{
5259
{ "א", "ב", "ג", "ד", "ה", "ו", "ז", "ח", "ט" },
5360
{ "י", "כ", "ל", "מ", "נ", "ס", "ע", "פ", "צ" },
54-
{ "ק", "ר", "ש", "ת", "תק", "תר", "תש", "תת", "תתק", }
61+
{ "ק", "ר", "ש", "ת", "תק", "תר", "תש", "תת", "תתק", },
62+
{ "א׳", "ב׳", "ג׳", "ד׳", "ה׳", "ו׳", "ז׳", "ח׳", "ט׳" }
5563
};
5664

5765
private static readonly string[,] _georgianDigitsTable =
@@ -61,11 +69,18 @@ internal static class CommonUtils
6169
{ "რ", "ს", "ტ", "ჳ", "ფ", "ქ", "ღ", "ყ", "შ" }
6270
};
6371

72+
/// <summary>
73+
/// Table to convert numbers into armenian digits - CSS Counter Styles Level 3 §6.1's
74+
/// "armenian" <c>@counter-style</c>. Row 3 (thousands) added for the same reason as
75+
/// <see cref="_hebrewDigitsTable"/>'s - a 3-row table silently drops the thousands digit for
76+
/// any value >= 1000.
77+
/// </summary>
6478
private static readonly string[,] _armenianDigitsTable =
6579
{
6680
{ "Ա", "Բ", "Գ", "Դ", "Ե", "Զ", "Է", "Ը", "Թ" },
6781
{ "Ժ", "Ի", "Լ", "Խ", "Ծ", "Կ", "Հ", "Ձ", "Ղ" },
68-
{ "Ճ", "Մ", "Յ", "Ն", "Շ", "Ո", "Չ", "Պ", "Ջ" }
82+
{ "Ճ", "Մ", "Յ", "Ն", "Շ", "Ո", "Չ", "Պ", "Ջ" },
83+
{ "Ռ", "Ս", "Վ", "Տ", "Ր", "Ց", "Ւ", "Փ", "Ք" }
6984
};
7085

7186
private static readonly string[] _hiraganaDigitsTable = new[]
@@ -378,7 +393,7 @@ public static string ConvertToAlphaNumber(int number, string style = CssConstant
378393
}
379394
else if (style.Equals(CssConstants.Hebrew, StringComparison.InvariantCultureIgnoreCase))
380395
{
381-
return ConvertToSpecificNumbers(number, _hebrewDigitsTable);
396+
return ConvertToHebrewNumber(number);
382397
}
383398
else if (style.Equals(CssConstants.Hiragana, StringComparison.InvariantCultureIgnoreCase) || style.Equals(CssConstants.HiraganaIroha, StringComparison.InvariantCultureIgnoreCase))
384399
{
@@ -469,6 +484,27 @@ private static string ConvertToRomanNumbers(int number, bool lowercase)
469484
return lowercase ? sb.ToLower() : sb;
470485
}
471486

487+
/// <summary>
488+
/// Hebrew's additive algorithm (<see cref="ConvertToSpecificNumbers"/> over
489+
/// <see cref="_hebrewDigitsTable"/>) with the CSS Counter Styles Level 3 §6.1 override for a
490+
/// tens-and-units value of 15 or 16: the naive per-digit-group combination would produce יה/יו,
491+
/// which closely resembles the Tetragrammaton, so the spec mandates טו/טז instead. 17-19 don't
492+
/// need an override - their naive combination (יז/יח/יט) already matches the spec's table.
493+
/// </summary>
494+
/// <param name="number">the number to convert</param>
495+
/// <returns>the hebrew number string</returns>
496+
private static string ConvertToHebrewNumber(int number)
497+
{
498+
var tensAndUnits = number % 100;
499+
if (tensAndUnits is 15 or 16)
500+
{
501+
var hundredsAndUp = ConvertToSpecificNumbers(number - tensAndUnits, _hebrewDigitsTable);
502+
return hundredsAndUp + (tensAndUnits == 15 ? "טו" : "טז");
503+
}
504+
505+
return ConvertToSpecificNumbers(number, _hebrewDigitsTable);
506+
}
507+
472508
/// <summary>
473509
/// Convert the given integer into given alphabet numeric system.
474510
/// </summary>

‎Source/HtmlRenderer/Core/Utils/CssUtils.cs‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,13 @@ public static void SetPropertyValue(CssBox cssBox, string propName, string value
394394
cssBox.WordSpacing = value;
395395
break;
396396
case "font-family":
397-
cssBox.FontFamily = value;
397+
// The declared value may be a comma-separated fallback list and/or have quoted
398+
// candidates (e.g. "'Helvetica Neue', Arial, sans-serif") - resolve it down to the
399+
// first candidate that's actually installed, same as the legacy <font face="...">
400+
// attribute already does, rather than handing the literal, possibly-quoted CSS
401+
// text straight to font creation (which fails to match any real font by name and
402+
// silently falls back to a generic system font for every unresolved candidate).
403+
cssBox.FontFamily = cssBox.HtmlContainer.CssParser.ParseFontFamily(value);
398404
break;
399405
case "font-size":
400406
cssBox.FontSize = value;

0 commit comments

Comments
 (0)