Skip to content

Commit 9b53b22

Browse files
committed
Improve font selection mechanism by comparing all available font families
DEVSIX-7757
1 parent cd1e2d2 commit 9b53b22

File tree

11 files changed

+233
-8
lines changed

11 files changed

+233
-8
lines changed

io/src/main/java/com/itextpdf/io/font/FontNames.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,10 @@ public class FontNames {
3636

3737
// name, ID = 4
3838
private String[][] fullName;
39-
// name, ID = 1 or 16
39+
// name, ID = 16 or 1
4040
private String[][] familyName;
41+
// name, ID = 1
42+
private String[][] familyName2;
4143
// name, ID = 2 or 17
4244
private String[][] subfamily;
4345
//name, ID = 6
@@ -82,6 +84,15 @@ public String[][] getFamilyName() {
8284
return familyName;
8385
}
8486

87+
/**
88+
* Get extra family name if exists.
89+
*
90+
* @return extra family name if exists in the font, {@code null} otherwise.
91+
*/
92+
public String[][] getFamilyName2() {
93+
return familyName2;
94+
}
95+
8596
public String getStyle() {
8697
return style;
8798
}
@@ -176,6 +187,15 @@ protected void setFamilyName(String[][] familyName) {
176187
this.familyName = familyName;
177188
}
178189

190+
/**
191+
* Set extra family name used for better fonts match.
192+
*
193+
* @param familyName2 family name to set.
194+
*/
195+
protected void setFamilyName2(String[][] familyName2) {
196+
this.familyName2 = familyName2;
197+
}
198+
179199
protected void setFamilyName(String familyName) {
180200
this.familyName = new String[][]{new String[]{"", "", "", familyName}};
181201
}

io/src/main/java/com/itextpdf/io/font/FontProgramDescriptor.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public class FontProgramDescriptor {
3737
private final String fullNameLowerCase;
3838
private final String fontNameLowerCase;
3939
private final String familyNameLowerCase;
40+
private final String familyName2LowerCase;
4041

4142
private final String style;
4243
private final int macStyle;
@@ -63,7 +64,14 @@ public class FontProgramDescriptor {
6364
this.fontName = fontNames.getFontName();
6465
this.fontNameLowerCase = this.fontName.toLowerCase();
6566
this.fullNameLowerCase = fontNames.getFullName()[0][3].toLowerCase();
66-
this.familyNameLowerCase = fontNames.getFamilyName() != null && fontNames.getFamilyName()[0][3] != null ? fontNames.getFamilyName()[0][3].toLowerCase() : null;
67+
this.familyNameLowerCase = fontNames.getFamilyName() != null && fontNames.getFamilyName()[0][3] != null ?
68+
fontNames.getFamilyName()[0][3].toLowerCase() : null;
69+
// For font family2 let's take the last element in array. The family in the 1st element has high chance
70+
// to be the same as returned by getFamilyName. Ideally we should take different families based on OS
71+
// but it breaks the compatibility, produces different results on different OSs etc.
72+
String[][] familyName2 = fontNames.getFamilyName2();
73+
this.familyName2LowerCase = familyName2 != null && familyName2[familyName2.length - 1][3] != null ?
74+
familyName2[familyName2.length - 1][3].toLowerCase() : null;
6775
this.style = fontNames.getStyle();
6876
this.weight = fontNames.getFontWeight();
6977
this.macStyle = fontNames.getMacStyle();
@@ -118,6 +126,15 @@ public String getFamilyNameLowerCase() {
118126
return familyNameLowerCase;
119127
}
120128

129+
/**
130+
* Get extra family name if exists.
131+
*
132+
* @return extra family name if exists in the font, {@code null} otherwise.
133+
*/
134+
public String getFamilyName2LowerCase() {
135+
return familyName2LowerCase;
136+
}
137+
121138
public Set<String> getFullNameAllLangs() { return fullNamesAllLangs; }
122139

123140
public Set<String> getFullNamesEnglishOpenType() { return fullNamesEnglishOpenType; }

io/src/main/java/com/itextpdf/io/font/OpenTypeParser.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,10 +271,12 @@ public FontNames getFontNames() {
271271
fontNames.setFontName(getPsFontName());
272272
fontNames.setFullName(fontNames.getNames(4));
273273
String[][] otfFamilyName = fontNames.getNames(16);
274+
String[][] familyName = fontNames.getNames(1);
275+
fontNames.setFamilyName2(familyName);
274276
if (otfFamilyName != null) {
275277
fontNames.setFamilyName(otfFamilyName);
276278
} else {
277-
fontNames.setFamilyName(fontNames.getNames(1));
279+
fontNames.setFamilyName(familyName);
278280
}
279281
String[][] subfamily = fontNames.getNames(2);
280282
if (subfamily != null) {

layout/src/main/java/com/itextpdf/layout/font/FontSelector.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,16 @@ public int compare(FontInfo o1, FontInfo o2) {
117117
fc.setMonospaceFlag(true);
118118
}
119119
boolean isLastFontFamilyToBeProcessed = i == fontFamilies.size() - 1;
120-
res = characteristicsSimilarity(fontFamily, fc, o2, isLastFontFamilyToBeProcessed) - characteristicsSimilarity(fontFamily, fc, o1, isLastFontFamilyToBeProcessed);
120+
res = characteristicsSimilarity(fontFamily, fc, o2, isLastFontFamilyToBeProcessed) -
121+
characteristicsSimilarity(fontFamily, fc, o1, isLastFontFamilyToBeProcessed);
122+
// This method is a fallback to compare family2 field if the main method wasn't able to prioritize
123+
// the fonts. We don't want to add this into scoring in the main method (characteristicsSimilarity)
124+
// not to break anything for existing solutions.
125+
if (res == 0) {
126+
res = family2Similarity(fontFamily, fc, o2) - family2Similarity(fontFamily, fc, o1);
127+
}
121128
}
129+
122130
return res;
123131
}
124132

@@ -227,5 +235,19 @@ private static int characteristicsSimilarity(String fontFamily, FontCharacterist
227235

228236
return score;
229237
}
238+
239+
/**
240+
* This method is a fallback to compare family2 field if the main method wasn't able to prioritize the fonts.
241+
*/
242+
private static int family2Similarity(String fontFamily, FontCharacteristics fc, FontInfo fontInfo) {
243+
FontProgramDescriptor fontDescriptor = fontInfo.getDescriptor();
244+
if (!fc.isMonospace() && null == fontInfo.getAlias() &&
245+
null != fontDescriptor.getFamilyName2LowerCase() &&
246+
fontDescriptor.getFamilyName2LowerCase().equals(fontFamily)) {
247+
return 1;
248+
}
249+
250+
return 0;
251+
}
230252
}
231253
}

layout/src/test/java/com/itextpdf/layout/FontSelectorTest.java

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,7 @@ public void openSansFontSetIncorrectNameTest01() {
647647
fc = new FontCharacteristics();
648648
fc.setFontStyle("italic");
649649
fc.setFontWeight((short) 500);
650-
assertSelectedFont(fontInfoCollection, fontFamilies, fc, "OpenSans-LightItalic");
650+
assertSelectedFont(fontInfoCollection, fontFamilies, fc, "OpenSans-Italic");
651651

652652
fc = new FontCharacteristics();
653653
fc.setFontStyle("oblique");
@@ -742,7 +742,7 @@ public void openSansFontSetRegularTest01() {
742742
fc = new FontCharacteristics();
743743
fc.setFontStyle("italic");
744744
fc.setFontWeight((short) 500);
745-
assertSelectedFont(fontInfoCollection, fontFamilies, fc, "OpenSans-LightItalic");
745+
assertSelectedFont(fontInfoCollection, fontFamilies, fc, "OpenSans-Italic");
746746

747747
fc = new FontCharacteristics();
748748
fc.setFontStyle("oblique");
@@ -838,7 +838,7 @@ public void openSansFontSetLightTest01() {
838838
fc = new FontCharacteristics();
839839
fc.setFontStyle("italic");
840840
fc.setFontWeight((short) 500);
841-
assertSelectedFont(fontInfoCollection, fontFamilies, fc, "OpenSans-LightItalic");
841+
assertSelectedFont(fontInfoCollection, fontFamilies, fc, "OpenSans-Italic");
842842

843843
fc = new FontCharacteristics();
844844
fc.setFontStyle("oblique");
@@ -934,7 +934,7 @@ public void openSansFontSetExtraBoldTest01() {
934934
fc = new FontCharacteristics();
935935
fc.setFontStyle("italic");
936936
fc.setFontWeight((short) 500);
937-
assertSelectedFont(fontInfoCollection, fontFamilies, fc, "OpenSans-LightItalic");
937+
assertSelectedFont(fontInfoCollection, fontFamilies, fc, "OpenSans-Italic");
938938

939939
fc = new FontCharacteristics();
940940
fc.setFontStyle("oblique");
@@ -963,6 +963,19 @@ public void openSansFontSetExtraBoldTest01() {
963963
assertSelectedFont(fontInfoCollection, fontFamilies, fc, "OpenSans-ExtraBoldItalic");
964964
}
965965

966+
@Test
967+
public void openSansLightTest() {
968+
FontSet set = getOpenSansFontSet();
969+
addTimesFonts(set);
970+
Collection<FontInfo> fontInfoCollection = set.getFonts();
971+
List<String> fontFamilies = new ArrayList<>();
972+
fontFamilies.add("Open Sans Light");
973+
974+
FontCharacteristics fc = new FontCharacteristics();
975+
fc.setFontWeight((short) 500);
976+
assertSelectedFont(fontInfoCollection, fontFamilies, fc, "OpenSans-Light");
977+
}
978+
966979
@Test
967980
public void openSansFontWeightBoldRenderingTest() throws Exception {
968981
String outFileName = destinationFolder + "openSansFontWeightBoldRendering.pdf";
@@ -1108,6 +1121,24 @@ public void monospaceFontIsNotSelectedInPreferenceToTestFamilyTest() {
11081121
new FontSelector(set.getFonts(), fontFamilies, fc).bestMatch().getDescriptor().getFontName());
11091122
}
11101123

1124+
@Test
1125+
public void family2UsedToSortFontsTest() {
1126+
FontSet set = new FontSet();
1127+
set.addFont(fontsFolder + "Lato/Lato-Black.ttf");
1128+
set.addFont(fontsFolder + "Lato/Lato-Regular.ttf");
1129+
set.addFont(fontsFolder + "Lato/Lato-Italic.ttf");
1130+
set.addFont(fontsFolder + "Lato/Lato-Hairline.ttf");
1131+
1132+
List<String> fontFamilies = new ArrayList<>();
1133+
fontFamilies.add("Lato Hairline");
1134+
1135+
FontCharacteristics fc = new FontCharacteristics();
1136+
fc.setFontWeight((short) 300); // Between hairline (200) and regular (400)
1137+
1138+
Assert.assertEquals("Lato-Hairline",
1139+
new FontSelector(set.getFonts(), fontFamilies, fc).bestMatch().getDescriptor().getFontName());
1140+
}
1141+
11111142
private void checkSelector(Collection<FontInfo> fontInfoCollection, String fontFamily,
11121143
String expectedNormal, String expectedBold, String expectedItalic, String expectedBoldItalic) {
11131144
List<String> fontFamilies = new ArrayList<>();
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
Copyright (c) 2011, TypeTogether (www.type-together.com),
2+
with Reserved Font Names "Abril" and "Abril Fatface"
3+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
4+
This license is copied below, and is also available with a FAQ at:
5+
http://scripts.sil.org/OFL
6+
7+
8+
-----------------------------------------------------------
9+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10+
-----------------------------------------------------------
11+
12+
PREAMBLE
13+
The goals of the Open Font License (OFL) are to stimulate worldwide
14+
development of collaborative font projects, to support the font creation
15+
efforts of academic and linguistic communities, and to provide a free and
16+
open framework in which fonts may be shared and improved in partnership
17+
with others.
18+
19+
The OFL allows the licensed fonts to be used, studied, modified and
20+
redistributed freely as long as they are not sold by themselves. The
21+
fonts, including any derivative works, can be bundled, embedded,
22+
redistributed and/or sold with any software provided that any reserved
23+
names are not used by derivative works. The fonts and derivatives,
24+
however, cannot be released under any other type of license. The
25+
requirement for fonts to remain under this license does not apply
26+
to any document created using the fonts or their derivatives.
27+
28+
DEFINITIONS
29+
"Font Software" refers to the set of files released by the Copyright
30+
Holder(s) under this license and clearly marked as such. This may
31+
include source files, build scripts and documentation.
32+
33+
"Reserved Font Name" refers to any names specified as such after the
34+
copyright statement(s).
35+
36+
"Original Version" refers to the collection of Font Software components as
37+
distributed by the Copyright Holder(s).
38+
39+
"Modified Version" refers to any derivative made by adding to, deleting,
40+
or substituting -- in part or in whole -- any of the components of the
41+
Original Version, by changing formats or by porting the Font Software to a
42+
new environment.
43+
44+
"Author" refers to any designer, engineer, programmer, technical
45+
writer or other person who contributed to the Font Software.
46+
47+
PERMISSION & CONDITIONS
48+
Permission is hereby granted, free of charge, to any person obtaining
49+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
50+
redistribute, and sell modified and unmodified copies of the Font
51+
Software, subject to the following conditions:
52+
53+
1) Neither the Font Software nor any of its individual components,
54+
in Original or Modified Versions, may be sold by itself.
55+
56+
2) Original or Modified Versions of the Font Software may be bundled,
57+
redistributed and/or sold with any software, provided that each copy
58+
contains the above copyright notice and this license. These can be
59+
included either as stand-alone text files, human-readable headers or
60+
in the appropriate machine-readable metadata fields within text or
61+
binary files as long as those fields can be easily viewed by the user.
62+
63+
3) No Modified Version of the Font Software may use the Reserved Font
64+
Name(s) unless explicit written permission is granted by the corresponding
65+
Copyright Holder. This restriction only applies to the primary font name as
66+
presented to the users.
67+
68+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69+
Software shall not be used to promote, endorse or advertise any
70+
Modified Version, except to acknowledge the contribution(s) of the
71+
Copyright Holder(s) and the Author(s) or with their explicit written
72+
permission.
73+
74+
5) The Font Software, modified or unmodified, in part or in whole,
75+
must be distributed entirely under this license, and must not be
76+
distributed under any other license. The requirement for fonts to
77+
remain under this license does not apply to any document created
78+
using the Font Software.
79+
80+
TERMINATION
81+
This license becomes null and void if any of the above conditions are
82+
not met.
83+
84+
DISCLAIMER
85+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93+
OTHER DEALINGS IN THE FONT SOFTWARE.

0 commit comments

Comments
 (0)