Skip to content

Commit 766f93d

Browse files
authored
Merge pull request #613 from danfickle/alt_612_better_font_importing
Fixes #608, fixes #405 - Allow add fonts as files instead of input streams
2 parents 7667c31 + 4af4f08 commit 766f93d

File tree

13 files changed

+402
-154
lines changed

13 files changed

+402
-154
lines changed

openhtmltopdf-core/src/main/java/com/openhtmltopdf/extend/SVGDrawer.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
package com.openhtmltopdf.extend;
22

3+
import java.awt.FontFormatException;
34
import java.io.Closeable;
5+
import java.io.File;
6+
import java.io.IOException;
47
import java.util.List;
58

69
import org.w3c.dom.Element;
710

811
import com.openhtmltopdf.css.sheet.FontFaceRule;
912
import com.openhtmltopdf.css.style.CssContext;
1013
import com.openhtmltopdf.layout.SharedContext;
14+
import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder.FontStyle;
1115
import com.openhtmltopdf.render.Box;
1216
import com.openhtmltopdf.render.RenderingContext;
1317

@@ -28,4 +32,6 @@ interface SVGImage {
2832
void drawSVG(OutputDevice outputDevice, RenderingContext ctx,
2933
double x, double y);
3034
}
35+
36+
void addFontFile(File fontFile, String family, Integer weight, FontStyle style) throws IOException, FontFormatException;
3137
}

openhtmltopdf-core/src/main/java/com/openhtmltopdf/outputdevice/helper/AddedFont.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
import java.io.File;
44
import java.io.InputStream;
5+
import java.util.Set;
56

67
import com.openhtmltopdf.extend.FSSupplier;
8+
import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder.FSFontUseCase;
79
import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder.FontStyle;
810

911
public class AddedFont {
@@ -14,25 +16,40 @@ public class AddedFont {
1416
public final boolean subset;
1517
public final FontStyle style;
1618
public final Object pdfontSupplier; // Bit of a hack, not type-safe!
19+
public final Set<FSFontUseCase> usedFor;
1720

18-
public AddedFont(FSSupplier<InputStream> supplier, File fontFile, Integer weight, String family, boolean subset,
19-
FontStyle style) {
21+
public AddedFont(
22+
FSSupplier<InputStream> supplier,
23+
File fontFile,
24+
Integer weight,
25+
String family,
26+
boolean subset,
27+
FontStyle style,
28+
Set<FSFontUseCase> usedFor) {
2029
this.supplier = supplier;
2130
this.fontFile = fontFile;
2231
this.pdfontSupplier = null;
2332
this.weight = weight;
2433
this.family = family;
2534
this.subset = subset;
2635
this.style = style;
36+
this.usedFor = usedFor;
2737
}
2838

29-
public AddedFont(Object pdfontSupplier, Integer weight, String family, boolean subset, FontStyle style) {
39+
public AddedFont(
40+
Object pdfontSupplier,
41+
Integer weight,
42+
String family,
43+
boolean subset,
44+
FontStyle style,
45+
Set<FSFontUseCase> usedFor) {
3046
this.supplier = null;
3147
this.fontFile = null;
3248
this.pdfontSupplier = pdfontSupplier;
3349
this.weight = weight;
3450
this.family = family;
3551
this.subset = subset;
3652
this.style = style;
53+
this.usedFor = usedFor;
3754
}
3855
}

openhtmltopdf-core/src/main/java/com/openhtmltopdf/outputdevice/helper/BaseRendererBuilder.java

Lines changed: 73 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.io.File;
1515
import java.io.InputStream;
1616
import java.util.ArrayList;
17+
import java.util.EnumSet;
1718
import java.util.HashMap;
1819
import java.util.List;
1920
import java.util.Map;
@@ -453,61 +454,98 @@ public final TFinalClass useFastMode() {
453454
}
454455

455456
/**
456-
* Like {@link #useFont(FSSupplier, String, Integer, FontStyle, boolean)}, but
457-
* allows to supply a font file. If the font file is a .ttc file it is handled
458-
* as TrueTypeCollection (PDF only). If you have the font in file form you should use this
459-
* API.
457+
* <p>Allows the user to provide a font file for use by the main
458+
* document only (not SVGs). See:
459+
* {@link #useFont(File, String, Integer, FontStyle, boolean, Set)}</p>
460+
*
461+
* <p>For gotchas related to font handling please see:
462+
* <a href="https://github.com/danfickle/openhtmltopdf/wiki/Fonts">Wiki: Fonts</a></p>
460463
*/
461-
public TFinalClass useFont(File fontFile, String fontFamily, Integer fontWeight, FontStyle fontStyle,
464+
public TFinalClass useFont(
465+
File fontFile,
466+
String fontFamily,
467+
Integer fontWeight,
468+
FontStyle fontStyle,
462469
boolean subset) {
463-
state._fonts.add(new AddedFont(null, fontFile, fontWeight, fontFamily, subset, fontStyle));
470+
state._fonts.add(new AddedFont(null, fontFile, fontWeight, fontFamily, subset, fontStyle, EnumSet.of(FSFontUseCase.DOCUMENT)));
471+
return (TFinalClass) this;
472+
}
473+
474+
/**
475+
* <p>Allows the user to provide a font file for use any or all of
476+
* the use cases listed in {@link FSFontUseCase} such as main
477+
* document, SVGs, etc.</p>
478+
*
479+
* <p>For gotchas related to font handling please see:
480+
* <a href="https://github.com/danfickle/openhtmltopdf/wiki/Fonts">Wiki: Fonts</a></p>
481+
*
482+
* @param fontFile A file system font file in true-type format. Beware of using resources as
483+
* they will not be separate files in the final jar.
484+
* @param fontFamily Font family name. If using a font in Java2D, SVG or MathML this should match
485+
* <code>Font.createFont(Font.TRUETYPE_FONT, fontFile).getFamily()</code>.
486+
* @param fontWeight Font boldness, usually 400 for regular fonts and 700 for bold fonts.
487+
* @param fontStyle Normal, italic or oblique.
488+
* @param subset For PDF use whether the font is subset, usually true unless the font is
489+
* being used by form controls.
490+
* @param fontUsedFor Which components use the font such as main document, SVG, etc. Example:
491+
* <code>EnumSet.of(FSFontUseCase.DOCUMENT, FSFontUseCase.SVG)</code>
492+
* @return this for method chaining
493+
*/
494+
public TFinalClass useFont(
495+
File fontFile,
496+
String fontFamily,
497+
Integer fontWeight,
498+
FontStyle fontStyle,
499+
boolean subset,
500+
Set<FSFontUseCase> fontUsedFor) {
501+
state._fonts.add(new AddedFont(null, fontFile, fontWeight, fontFamily, subset, fontStyle, fontUsedFor));
464502
return (TFinalClass) this;
465503
}
466504

467505
/**
468506
* Simpler overload for
469507
* {@link #useFont(File, String, Integer, FontStyle, boolean)}
470508
*
471-
* @param fontFile
472-
* @param fontFamily
473509
* @return this for method chaining
474510
*/
475511
public TFinalClass useFont(File fontFile, String fontFamily) {
476512
return this.useFont(fontFile, fontFamily, 400, FontStyle.NORMAL, true);
477513
}
478-
514+
479515
/**
480-
* Add a font programmatically. If the font is NOT subset, it will be downloaded
516+
* <p>Add a font programmatically. If the font is NOT subset, it will be downloaded
481517
* when the renderer is run, otherwise, assuming a font-metrics cache has been configured,
482518
* the font will only be downloaded if required. Therefore, the user could add many fonts,
483-
* confident that only those that are needed will be downloaded and processed.
519+
* confident that only those that are needed will be downloaded and processed.</p>
484520
*
485-
* The InputStream returned by the supplier will be closed by the caller. Fonts
486-
* should generally be subset (Java2D rendered ignores this argument),
487-
* except when used in form controls. FSSupplier is a lambda compatible interface.
521+
* <p>The InputStream returned by the supplier will be closed by the caller. Fonts
522+
* should generally be subset (Java2D renderer ignores this argument),
523+
* except when used in form controls. FSSupplier is a lambda compatible interface.</p>
488524
*
489-
* Fonts can also be added using a font-face at-rule in the CSS.
525+
* <p>Fonts can also be added using a font-face at-rule in the CSS (not
526+
* recommended for Java2D usage).</p>
527+
*
528+
* <p><strong>IMPORTANT:</strong> This method will add fonts for use by the main document
529+
* only. It is not recommended for use with Java2D.
530+
* To add fonts for use by Java2D, SVG, etc see:
531+
* {@link #useFont(File, String, Integer, FontStyle, boolean, Set)}</p>
532+
*
533+
* <p>For gotchas related to font handling please see:
534+
* <a href="https://github.com/danfickle/openhtmltopdf/wiki/Fonts">Wiki: Fonts</a></p>
490535
*
491-
* @param supplier
492-
* @param fontFamily
493-
* @param fontWeight
494-
* @param fontStyle
495-
* @param subset
496-
* @return
536+
* @return this for method chaining
497537
*/
498538
public TFinalClass useFont(FSSupplier<InputStream> supplier, String fontFamily, Integer fontWeight,
499539
FontStyle fontStyle, boolean subset) {
500-
state._fonts.add(new AddedFont(supplier, null, fontWeight, fontFamily, subset, fontStyle));
540+
state._fonts.add(new AddedFont(supplier, null, fontWeight, fontFamily, subset, fontStyle, EnumSet.of(FSFontUseCase.DOCUMENT)));
501541
return (TFinalClass) this;
502542
}
503543

504544
/**
505545
* Simpler overload for
506546
* {@link #useFont(FSSupplier, String, Integer, FontStyle, boolean)}
507547
*
508-
* @param supplier
509-
* @param fontFamily
510-
* @return
548+
* @return this for method chaining
511549
*/
512550
public TFinalClass useFont(FSSupplier<InputStream> supplier, String fontFamily) {
513551
return this.useFont(supplier, fontFamily, 400, FontStyle.NORMAL, true);
@@ -533,4 +571,14 @@ public enum PageSizeUnits {
533571
public enum FontStyle {
534572
NORMAL, ITALIC, OBLIQUE
535573
}
574+
575+
/**
576+
* Use cases for fonts.
577+
*/
578+
public enum FSFontUseCase {
579+
/** Main document (PDF or Java2D) */
580+
DOCUMENT,
581+
SVG,
582+
MATHML
583+
}
536584
}
Binary file not shown.

openhtmltopdf-examples/src/main/resources/visualtest/html/replaced-sizing-mathml.html

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,6 @@
44
<html>
55
<head>
66
<style>
7-
@font-face {
8-
src: url(fonts/Karla-Bold.ttf);
9-
font-family: 'MyFont';
10-
font-weight: normal;
11-
}
127
@page {
138
size: 500px 1500px;
149
margin: 0;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<html>
2+
<head>
3+
<style>
4+
@page {
5+
size: 300px 300px;
6+
}
7+
svg {
8+
border: 1px solid red;
9+
width: 100px;
10+
height: 100px;
11+
}
12+
</style>
13+
</head>
14+
<body>
15+
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
16+
<text y="40" x="10" font-family="Karla" font-weight="bold" font-size="32">Karla Bold</text>
17+
<circle cx="50" cy="80" r="20"/>
18+
</svg>
19+
</body>
20+
</html>

openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
package com.openhtmltopdf.visualregressiontests;
22

3+
import java.awt.FontFormatException;
34
import java.io.*;
45

56
import static org.junit.Assert.assertTrue;
67

78
import java.nio.charset.StandardCharsets;
89
import java.util.Collections;
10+
import java.util.EnumSet;
911

1012
import com.openhtmltopdf.bidi.support.ICUBidiReorderer;
1113
import com.openhtmltopdf.bidi.support.ICUBidiSplitter;
1214
import com.openhtmltopdf.extend.FSStream;
1315
import com.openhtmltopdf.objects.zxing.ZXingObjectDrawer;
16+
import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder.FSFontUseCase;
17+
import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder.FontStyle;
18+
1419
import org.junit.Before;
20+
import org.junit.BeforeClass;
1521
import org.junit.Ignore;
1622
import org.junit.Test;
1723
import com.openhtmltopdf.latexsupport.LaTeXDOMMutator;
@@ -27,7 +33,12 @@
2733

2834
public class VisualRegressionTest {
2935
private VisualTester vt;
30-
36+
37+
@BeforeClass
38+
public static void configureTests() throws IOException {
39+
TestSupport.makeFontFiles();
40+
}
41+
3142
@Before
3243
public void configureTester() {
3344
File outputDirectory = new File("target/test/visual-tests/test-output/");
@@ -831,6 +842,9 @@ public void testReplacedSizingSvgNonCss() throws IOException {
831842
public void testReplacedSizingMathMl() throws IOException {
832843
assertTrue(vt.runTest("replaced-sizing-mathml", (builder) -> {
833844
builder.useMathMLDrawer(new MathMLDrawer());
845+
builder.useFont(
846+
new File("target/test/visual-tests/Karla-Bold.ttf"),
847+
"MyFont", 400, FontStyle.NORMAL, true, EnumSet.of(FSFontUseCase.MATHML));
834848
}));
835849
}
836850

@@ -1295,6 +1309,23 @@ public void testPr610ForcePageBreakLine() throws IOException {
12951309
assertTrue(vt.runTest("pr-610-force-page-break-line"));
12961310
}
12971311

1312+
1313+
/**
1314+
* Tests that a font can be added as a file for use with SVGs.
1315+
*/
1316+
@Test
1317+
public void testSVGFontFileAddition() throws IOException, FontFormatException {
1318+
assertTrue(vt.runTest("svg-font-file-addition",
1319+
builder -> {
1320+
TestSupport.WITH_SVG.configure(builder);
1321+
builder.useFont(
1322+
new File("target/test/visual-tests/Karla-Bold.ttf"),
1323+
"Karla",
1324+
700, FontStyle.NORMAL, true,
1325+
EnumSet.of(FSFontUseCase.SVG));
1326+
}));
1327+
}
1328+
12981329
// TODO:
12991330
// + Elements that appear just on generated overflow pages.
13001331
// + content property (page counters, etc)

0 commit comments

Comments
 (0)