Skip to content

Commit 7c42087

Browse files
Introduce #setFontFamily methods for setting preferred font families on elements
New setFontFamily methods replace the older setFont(String) method (which is deprecated now). The older method took a single String which was later processed by FontFamilySplitter. Some of the symbols in contents of this String argument were treated in a special way (like commas and quotes), also font family names with non-latin letters were previously ignored. New setFontFamily methods accept an array or list of Strings where every string is treated directly as desired font family name (without any preprocessing: all symbols are treated as a part of font-family name). DEVSIX-2525
1 parent 9dc56a7 commit 7c42087

File tree

15 files changed

+204
-46
lines changed

15 files changed

+204
-46
lines changed

io/src/main/java/com/itextpdf/io/LogMessageConstant.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ public final class LogMessageConstant {
9292
public static final String FONT_DICTIONARY_WITH_NO_FONT_DESCRIPTOR = "Font dictionary does not contain required /FontDescriptor entry.";
9393
public static final String FONT_DICTIONARY_WITH_NO_WIDTHS = "Font dictionary does not contain required /Widths entry.";
9494
public static final String FONT_HAS_INVALID_GLYPH = "Font {0} has invalid glyph: {1}";
95-
public static final String FONT_PROPERTY_MUST_BE_PDF_FONT_OBJECT = "The Font Property must be a PdfFont object";
95+
public static final String FONT_PROPERTY_MUST_BE_PDF_FONT_OBJECT = "The \"Property.FONT\" property must be a PdfFont object in this context.";
96+
public static final String FONT_PROPERTY_OF_STRING_TYPE_IS_DEPRECATED_USE_STRINGS_ARRAY_INSTEAD = "The \"Property.FONT\" property with values of String type is deprecated, use String[] as property value type instead.";
9697
public static final String FONT_SUBSET_ISSUE = "Font subset issue. Full font will be embedded.";
9798
public static final String FORBID_RELEASE_IS_SET = "ForbidRelease flag is set and release is called. Releasing will not be performed.";
9899
public static final String FORM_FIELD_WAS_FLUSHED = "A form field was flushed. There's no way to create this field in the AcroForm dictionary.";

layout/src/main/java/com/itextpdf/layout/ElementPropertyContainer.java

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ This file is part of the iText (R) project.
6363
import com.itextpdf.layout.splitting.ISplitCharacters;
6464

6565
import java.util.ArrayList;
66+
import java.util.Arrays;
6667
import java.util.HashMap;
6768
import java.util.List;
6869
import java.util.Map;
@@ -238,7 +239,9 @@ public T setHorizontalAlignment(HorizontalAlignment horizontalAlignment) {
238239

239240
/**
240241
* Sets the font of this Element.
241-
*
242+
* <p>
243+
* This property overrides the value set by {@link #setFontFamily}. Font is set either via exact {@link PdfFont}
244+
* instance or via font-family name that should correspond to the font in {@link FontProvider}, but not both.
242245
* @param font a {@link PdfFont font}
243246
* @return this Element.
244247
*/
@@ -247,14 +250,57 @@ public T setFont(PdfFont font) {
247250
return (T) (Object) this;
248251
}
249252

253+
/**
254+
* Sets the preferable font families for this Element.
255+
* Note that {@link com.itextpdf.layout.font.FontProvider} shall be set as well.
256+
* See {@link RootElement#setFontProvider(FontProvider)}
257+
* <p>
258+
* This property overrides the value set by {@link #setFont(PdfFont)}. Font is set either via exact {@link PdfFont}
259+
* instance or via font-family name that should correspond to the font in {@link FontProvider}, but not both.
260+
* <p>
261+
* All {@link String} that are passed as argument are directly handled as a collection of font family names,
262+
* without any pre-processing. Every font family name is treated as a preferable font-family to be used
263+
* inside the element. The {@code fontFamilyNames} argument is interpreted as as an ordered list,
264+
* where every next font-family should be used if font for the previous one was not found or doesn't contain required glyphs.
265+
* @see com.itextpdf.io.font.constants.StandardFontFamilies
266+
* @param fontFamilyNames defines an ordered list of preferable font families for this Element.
267+
* @return this Element.
268+
*/
269+
public T setFontFamily(String... fontFamilyNames) {
270+
setProperty(Property.FONT, fontFamilyNames);
271+
return (T) (Object) this;
272+
}
273+
274+
/**
275+
* Sets the preferable font families for this Element.
276+
* Note that {@link com.itextpdf.layout.font.FontProvider} shall be set as well.
277+
* See {@link RootElement#setFontProvider(FontProvider)}
278+
* <p>
279+
* This property overrides the value set by {@link #setFont(PdfFont)}. Font is set either via exact {@link PdfFont}
280+
* instance or via font-family name that should correspond to the font in {@link FontProvider}, but not both.
281+
* <p>
282+
* All {@link String} that are passed as argument are directly handled as a collection of font family names,
283+
* without any pre-processing. Every font family name is treated as a preferable font-family to be used
284+
* inside the element. The {@code fontFamilyNames} argument is interpreted as as an ordered list,
285+
* where every next font-family should be used if font for the previous one was not found or doesn't contain required glyphs.
286+
* @see com.itextpdf.io.font.constants.StandardFontFamilies
287+
* @param fontFamilyNames defines an ordered list of preferable font families for this Element.
288+
* @return this Element.
289+
*/
290+
public T setFontFamily(List<String> fontFamilyNames) {
291+
return this.setFontFamily(fontFamilyNames.toArray(new String[fontFamilyNames.size()]));
292+
}
293+
250294
/**
251295
* Sets the font family of this Element. Note that {@link com.itextpdf.layout.font.FontProvider} shall be set as well.
252296
* See {@link RootElement#setFontProvider(FontProvider)}
253297
*
254298
* @see com.itextpdf.io.font.constants.StandardFontFamilies
255299
* @param font a font name to fetch from {@link com.itextpdf.layout.font.FontProvider}
256300
* @return this Element.
301+
* @deprecated This method will be removed in 7.2. Use {@link #setFontFamily(String...)} instead.
257302
*/
303+
@Deprecated
258304
public T setFont(String font) {
259305
setProperty(Property.FONT, font);
260306
return (T) (Object) this;

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ This file is part of the iText (R) project.
4949

5050
/**
5151
* Split css font-family string into list of font-families or generic-families
52+
* @deprecated will be removed in iText 7.2.
5253
*/
54+
@Deprecated
5355
public final class FontFamilySplitter {
5456

5557
private static final Pattern FONT_FAMILY_PATTERN = Pattern.compile("^ *([\\w-]+) *$");

layout/src/main/java/com/itextpdf/layout/property/Property.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,12 @@ private Property() {
100100
public static final int FONT_KERNING = 22;
101101
/**
102102
* String value. 'normal'|'italic'|'oblique'
103-
* Note, this property will be applied only if {@link #FONT} has String value.
103+
* Note, this property will be applied only if {@link #FONT} has String[] value.
104104
*/
105105
public static final int FONT_STYLE = 94;
106106
/**
107107
* String value. 'normal'|'bold'|number
108-
* Note, this property will be applied only if {@link #FONT} has String value.
108+
* Note, this property will be applied only if {@link #FONT} has String[] value.
109109
*/
110110
public static final int FONT_WEIGHT = 95;
111111
public static final int FONT_SCRIPT = 23;

layout/src/main/java/com/itextpdf/layout/renderer/AbstractRenderer.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2153,15 +2153,22 @@ PdfFont resolveFirstPdfFont() {
21532153
Object font = this.<Object>getProperty(Property.FONT);
21542154
if (font instanceof PdfFont) {
21552155
return (PdfFont) font;
2156-
} else if (font instanceof String) {
2156+
} else if (font instanceof String || font instanceof String[]) {
2157+
if (font instanceof String) {
2158+
// TODO remove this if-clause before 7.2
2159+
Logger logger = LoggerFactory.getLogger(AbstractRenderer.class);
2160+
logger.warn(LogMessageConstant.FONT_PROPERTY_OF_STRING_TYPE_IS_DEPRECATED_USE_STRINGS_ARRAY_INSTEAD);
2161+
List<String> splitFontFamily = FontFamilySplitter.splitFontFamily((String) font);
2162+
font = splitFontFamily.toArray(new String[splitFontFamily.size()]);
2163+
}
21572164
FontProvider provider = this.<FontProvider>getProperty(Property.FONT_PROVIDER);
21582165
if (provider == null) {
21592166
throw new IllegalStateException("Invalid font type. FontProvider expected. Cannot resolve font with string value");
21602167
}
21612168
FontCharacteristics fc = createFontCharacteristics();
2162-
return resolveFirstPdfFont((String) font, provider, fc);
2169+
return resolveFirstPdfFont((String[]) font, provider, fc);
21632170
} else {
2164-
throw new IllegalStateException("String or PdfFont expected as value of FONT property");
2171+
throw new IllegalStateException("String[] or PdfFont expected as value of FONT property");
21652172
}
21662173
}
21672174

@@ -2170,8 +2177,8 @@ PdfFont resolveFirstPdfFont() {
21702177
// This method is intended to be called from previous method that deals with Font Property.
21712178
// NOTE: It neither change Font Property of renderer, nor is guarantied to contain all glyphs used in renderer.
21722179
// TODO this mechanism does not take text into account
2173-
PdfFont resolveFirstPdfFont(String font, FontProvider provider, FontCharacteristics fc) {
2174-
return provider.getPdfFont(provider.getFontSelector(FontFamilySplitter.splitFontFamily(font), fc).bestMatch());
2180+
PdfFont resolveFirstPdfFont(String[] font, FontProvider provider, FontCharacteristics fc) {
2181+
return provider.getPdfFont(provider.getFontSelector(Arrays.asList(font), fc).bestMatch());
21752182
}
21762183

21772184
static Border[] getBorders(IRenderer renderer) {

layout/src/main/java/com/itextpdf/layout/renderer/ListRenderer.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,9 @@ private LayoutResult initializeListSymbols(LayoutContext layoutContext) {
311311
++listItemNum;
312312
currentSymbolRenderer.setParent(childRenderers.get(i));
313313
// Workaround for the case when font is specified as string
314-
if (currentSymbolRenderer instanceof AbstractRenderer && currentSymbolRenderer.<Object>getProperty(Property.FONT) instanceof String) {
314+
if (currentSymbolRenderer instanceof AbstractRenderer
315+
// TODO remove check for String type before 7.2
316+
&& (currentSymbolRenderer.<Object>getProperty(Property.FONT) instanceof String[] || currentSymbolRenderer.<Object>getProperty(Property.FONT) instanceof String)) {
315317
PdfFont actualPdfFont = ((AbstractRenderer) currentSymbolRenderer).resolveFirstPdfFont();
316318
currentSymbolRenderer.setProperty(Property.FONT, actualPdfFont);
317319
}

layout/src/main/java/com/itextpdf/layout/renderer/TextRenderer.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ This file is part of the iText (R) project.
9090
import org.slf4j.LoggerFactory;
9191

9292
import java.util.ArrayList;
93+
import java.util.Arrays;
9394
import java.util.Collection;
9495
import java.util.Collections;
9596
import java.util.Iterator;
@@ -1196,7 +1197,7 @@ protected float calculateLineWidth() {
11961197
}
11971198

11981199
/**
1199-
* Resolve {@link Property#FONT} string value.
1200+
* Resolve {@link Property#FONT} String[] value.
12001201
*
12011202
* @param addTo add all processed renderers to.
12021203
* @return true, if new {@link TextRenderer} has been created.
@@ -1206,15 +1207,21 @@ protected boolean resolveFonts(List<IRenderer> addTo) {
12061207
if (font instanceof PdfFont) {
12071208
addTo.add(this);
12081209
return false;
1209-
} else if (font instanceof String) {
1210+
} else if (font instanceof String || font instanceof String[]) {
1211+
if (font instanceof String) {
1212+
// TODO remove this if-clause before 7.2
1213+
Logger logger = LoggerFactory.getLogger(AbstractRenderer.class);
1214+
logger.warn(LogMessageConstant.FONT_PROPERTY_OF_STRING_TYPE_IS_DEPRECATED_USE_STRINGS_ARRAY_INSTEAD);
1215+
List<String> splitFontFamily = FontFamilySplitter.splitFontFamily((String) font);
1216+
font = splitFontFamily.toArray(new String[splitFontFamily.size()]);
1217+
}
12101218
FontProvider provider = this.<FontProvider>getProperty(Property.FONT_PROVIDER);
12111219
FontSet fontSet = this.<FontSet>getProperty(Property.FONT_SET);
12121220
if (provider.getFontSet().isEmpty() && (fontSet == null || fontSet.isEmpty())) {
12131221
throw new IllegalStateException("Invalid font type. FontProvider and FontSet are empty. Cannot resolve font with string value.");
12141222
}
12151223
FontCharacteristics fc = createFontCharacteristics();
1216-
FontSelectorStrategy strategy = provider.getStrategy(strToBeConverted,
1217-
FontFamilySplitter.splitFontFamily((String) font), fc, fontSet);
1224+
FontSelectorStrategy strategy = provider.getStrategy(strToBeConverted, Arrays.asList((String[])font), fc, fontSet);
12181225
// process empty renderers because they can have borders or paddings with background to be drawn
12191226
if (null == strToBeConverted || strToBeConverted.isEmpty()) {
12201227
addTo.add(this);
@@ -1254,9 +1261,8 @@ static void updateRangeBasedOnRemovedCharacters(ArrayList<Integer> removedIds, i
12541261
}
12551262

12561263
@Override
1257-
PdfFont resolveFirstPdfFont(String font, FontProvider provider, FontCharacteristics fc) {
1258-
FontSelectorStrategy strategy = provider.getStrategy(strToBeConverted,
1259-
FontFamilySplitter.splitFontFamily((String) font), fc);
1264+
PdfFont resolveFirstPdfFont(String[] font, FontProvider provider, FontCharacteristics fc) {
1265+
FontSelectorStrategy strategy = provider.getStrategy(strToBeConverted, Arrays.asList(font), fc);
12601266
List<Glyph> resolvedGlyphs;
12611267
PdfFont currentFont;
12621268
//try to find first font that can render at least one glyph.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1141,7 +1141,7 @@ public void paragraphVerticalAlignmentTest01() throws IOException, InterruptedEx
11411141
doc.setFontProvider(fontProvider);
11421142

11431143
String loremIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
1144-
doc.add(new Paragraph(loremIpsum).setHeight(100).setVerticalAlignment(VerticalAlignment.MIDDLE).setBorder(new SolidBorder(3)).setFont(StandardFonts.TIMES_ROMAN));
1144+
doc.add(new Paragraph(loremIpsum).setHeight(100).setVerticalAlignment(VerticalAlignment.MIDDLE).setBorder(new SolidBorder(3)).setFontFamily(StandardFonts.TIMES_ROMAN));
11451145

11461146
doc.close();
11471147

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

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,60 +56,87 @@ public class FontFamilySplitterTest {
5656
@Test
5757
public void fontFamilySplitter() throws Exception {
5858
String fontFamilies =
59-
"'Puritan'\n" +
59+
"'Puritan'\n" +
6060
"Puritan\n" +
61+
6162
"'Pur itan'\n" +
6263
"Pur itan\n" +
64+
6365
"'Pur it an'\n" +
6466
"Pur it an\n" +
67+
6568
" \"Puritan\"\n" +
6669
"Puritan\n" +
70+
6771
" \"Pur itan\"\n" +
6872
"Pur itan\n" +
73+
6974
"\"Pur it an\"\n" +
7075
"Pur it an\n" +
76+
7177
"FreeSans\n" +
7278
"FreeSans\n" +
79+
7380
"'Puritan', FreeSans\n" +
7481
"Puritan; FreeSans\n" +
82+
7583
"'Pur itan' , FreeSans\n" +
7684
"Pur itan; FreeSans\n" +
85+
7786
" 'Pur it an' , FreeSans \n" +
7887
"Pur it an; FreeSans\n" +
88+
7989
"\"Puritan\", FreeSans\n" +
8090
"Puritan; FreeSans\n" +
91+
8192
"\"Pur itan\", FreeSans\n" +
8293
"Pur itan; FreeSans\n" +
94+
8395
"\"Pur it an\", FreeSans\n" +
8496
"Pur it an; FreeSans\n" +
97+
8598
"\"Puritan\"\n" +
8699
"Puritan\n" +
100+
87101
"'Free Sans',\n"+
88102
"Free Sans\n"+
103+
89104
"'Free-Sans',\n"+
90105
"Free-Sans\n"+
106+
91107
" 'Free-Sans' , Puritan\n"+
92108
"Free-Sans; Puritan\n"+
109+
93110
" \"Free-Sans\" , Puritan\n"+
94111
"Free-Sans; Puritan\n"+
112+
95113
" Free-Sans , Puritan\n"+
96114
"Free-Sans; Puritan\n"+
115+
97116
" Free-Sans\n"+
98117
"Free-Sans\n"+
118+
99119
"\"Puritan\", Free Sans\n" +
100120
"Puritan\n" +
121+
101122
"\"Puritan 2.0\"\n" +
102123
"-\n" +
124+
103125
"'Puritan' FreeSans\n" +
104126
"-\n" +
127+
105128
"Pur itan\n" +
106129
"-\n" +
130+
107131
"Pur it an\"\n" +
108132
"-\n" +
133+
109134
"\"Free Sans\n" +
110135
"-\n" +
136+
111137
"Pur it an'\n" +
112138
"-\n" +
139+
113140
"'Free Sans\n"+
114141
"-";
115142

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,15 @@ public void standardAndType3Fonts() throws Exception {
122122
doc.setFontProvider(sel);
123123

124124
Paragraph paragraph = new Paragraph("Next paragraph contains a triangle, actually Type 3 Font");
125-
paragraph.setProperty(Property.FONT, StandardFontFamilies.TIMES); // TODO DEVSIX-2136 Update of necessary
125+
paragraph.setProperty(Property.FONT, new String[] {StandardFontFamilies.TIMES});
126126
doc.add(paragraph);
127127

128128

129129
paragraph = new Paragraph("A");
130-
paragraph.setFont("CustomFont");
130+
paragraph.setFontFamily("CustomFont");
131131
doc.add(paragraph);
132132
paragraph = new Paragraph("Next paragraph");
133-
paragraph.setProperty(Property.FONT, StandardFonts.COURIER);
133+
paragraph.setProperty(Property.FONT, new String[] {StandardFonts.COURIER});
134134
doc.add(paragraph);
135135

136136
doc.close();
@@ -154,7 +154,7 @@ public void customFontProvider() throws Exception {
154154
Paragraph paragraph1 = new Paragraph("Default Helvetica should be selected.");
155155
doc.add(paragraph1);
156156

157-
Paragraph paragraph2 = new Paragraph("Default Helvetica should be selected.").setFont(StandardFonts.COURIER);
157+
Paragraph paragraph2 = new Paragraph("Default Helvetica should be selected.").setFontFamily(StandardFonts.COURIER);
158158
doc.add(paragraph2);
159159

160160
doc.close();
@@ -179,7 +179,8 @@ public void customFontProvider2() throws Exception {
179179
Document doc = new Document(pdfDoc);
180180
doc.setFontProvider(fontProvider);
181181

182-
Paragraph paragraph = new Paragraph("There is no default font (Helvetica) inside the used FontProvider's instance. So the first font, that has been added, should be selected. Here it's FreeSans.").setFont("ABRACADABRA_THERE_IS_NO_SUCH_FONT");
182+
Paragraph paragraph = new Paragraph("There is no default font (Helvetica) inside the used FontProvider's instance. So the first font, that has been added, should be selected. Here it's FreeSans.")
183+
.setFontFamily("ABRACADABRA_THERE_IS_NO_SUCH_FONT");
183184
doc.add(paragraph);
184185

185186
doc.close();

0 commit comments

Comments
 (0)