Skip to content

Allow add fonts as files instead of input streams #613

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.openhtmltopdf.extend;

import java.awt.FontFormatException;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.util.List;

import org.w3c.dom.Element;

import com.openhtmltopdf.css.sheet.FontFaceRule;
import com.openhtmltopdf.css.style.CssContext;
import com.openhtmltopdf.layout.SharedContext;
import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder.FontStyle;
import com.openhtmltopdf.render.Box;
import com.openhtmltopdf.render.RenderingContext;

Expand All @@ -28,4 +32,6 @@ interface SVGImage {
void drawSVG(OutputDevice outputDevice, RenderingContext ctx,
double x, double y);
}

void addFontFile(File fontFile, String family, Integer weight, FontStyle style) throws IOException, FontFormatException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import java.io.File;
import java.io.InputStream;
import java.util.Set;

import com.openhtmltopdf.extend.FSSupplier;
import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder.FSFontUseCase;
import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder.FontStyle;

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

public AddedFont(FSSupplier<InputStream> supplier, File fontFile, Integer weight, String family, boolean subset,
FontStyle style) {
public AddedFont(
FSSupplier<InputStream> supplier,
File fontFile,
Integer weight,
String family,
boolean subset,
FontStyle style,
Set<FSFontUseCase> usedFor) {
this.supplier = supplier;
this.fontFile = fontFile;
this.pdfontSupplier = null;
this.weight = weight;
this.family = family;
this.subset = subset;
this.style = style;
this.usedFor = usedFor;
}

public AddedFont(Object pdfontSupplier, Integer weight, String family, boolean subset, FontStyle style) {
public AddedFont(
Object pdfontSupplier,
Integer weight,
String family,
boolean subset,
FontStyle style,
Set<FSFontUseCase> usedFor) {
this.supplier = null;
this.fontFile = null;
this.pdfontSupplier = pdfontSupplier;
this.weight = weight;
this.family = family;
this.subset = subset;
this.style = style;
this.usedFor = usedFor;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -453,61 +454,98 @@ public final TFinalClass useFastMode() {
}

/**
* Like {@link #useFont(FSSupplier, String, Integer, FontStyle, boolean)}, but
* allows to supply a font file. If the font file is a .ttc file it is handled
* as TrueTypeCollection (PDF only). If you have the font in file form you should use this
* API.
* <p>Allows the user to provide a font file for use by the main
* document only (not SVGs). See:
* {@link #useFont(File, String, Integer, FontStyle, boolean, Set)}</p>
*
* <p>For gotchas related to font handling please see:
* <a href="https://github.com/danfickle/openhtmltopdf/wiki/Fonts">Wiki: Fonts</a></p>
*/
public TFinalClass useFont(File fontFile, String fontFamily, Integer fontWeight, FontStyle fontStyle,
public TFinalClass useFont(
File fontFile,
String fontFamily,
Integer fontWeight,
FontStyle fontStyle,
boolean subset) {
state._fonts.add(new AddedFont(null, fontFile, fontWeight, fontFamily, subset, fontStyle));
state._fonts.add(new AddedFont(null, fontFile, fontWeight, fontFamily, subset, fontStyle, EnumSet.of(FSFontUseCase.DOCUMENT)));
return (TFinalClass) this;
}

/**
* <p>Allows the user to provide a font file for use any or all of
* the use cases listed in {@link FSFontUseCase} such as main
* document, SVGs, etc.</p>
*
* <p>For gotchas related to font handling please see:
* <a href="https://github.com/danfickle/openhtmltopdf/wiki/Fonts">Wiki: Fonts</a></p>
*
* @param fontFile A file system font file in true-type format. Beware of using resources as
* they will not be separate files in the final jar.
* @param fontFamily Font family name. If using a font in Java2D, SVG or MathML this should match
* <code>Font.createFont(Font.TRUETYPE_FONT, fontFile).getFamily()</code>.
* @param fontWeight Font boldness, usually 400 for regular fonts and 700 for bold fonts.
* @param fontStyle Normal, italic or oblique.
* @param subset For PDF use whether the font is subset, usually true unless the font is
* being used by form controls.
* @param fontUsedFor Which components use the font such as main document, SVG, etc. Example:
* <code>EnumSet.of(FSFontUseCase.DOCUMENT, FSFontUseCase.SVG)</code>
* @return this for method chaining
*/
public TFinalClass useFont(
File fontFile,
String fontFamily,
Integer fontWeight,
FontStyle fontStyle,
boolean subset,
Set<FSFontUseCase> fontUsedFor) {
state._fonts.add(new AddedFont(null, fontFile, fontWeight, fontFamily, subset, fontStyle, fontUsedFor));
return (TFinalClass) this;
}

/**
* Simpler overload for
* {@link #useFont(File, String, Integer, FontStyle, boolean)}
*
* @param fontFile
* @param fontFamily
* @return this for method chaining
*/
public TFinalClass useFont(File fontFile, String fontFamily) {
return this.useFont(fontFile, fontFamily, 400, FontStyle.NORMAL, true);
}

/**
* Add a font programmatically. If the font is NOT subset, it will be downloaded
* <p>Add a font programmatically. If the font is NOT subset, it will be downloaded
* when the renderer is run, otherwise, assuming a font-metrics cache has been configured,
* the font will only be downloaded if required. Therefore, the user could add many fonts,
* confident that only those that are needed will be downloaded and processed.
* confident that only those that are needed will be downloaded and processed.</p>
*
* The InputStream returned by the supplier will be closed by the caller. Fonts
* should generally be subset (Java2D rendered ignores this argument),
* except when used in form controls. FSSupplier is a lambda compatible interface.
* <p>The InputStream returned by the supplier will be closed by the caller. Fonts
* should generally be subset (Java2D renderer ignores this argument),
* except when used in form controls. FSSupplier is a lambda compatible interface.</p>
*
* Fonts can also be added using a font-face at-rule in the CSS.
* <p>Fonts can also be added using a font-face at-rule in the CSS (not
* recommended for Java2D usage).</p>
*
* <p><strong>IMPORTANT:</strong> This method will add fonts for use by the main document
* only. It is not recommended for use with Java2D.
* To add fonts for use by Java2D, SVG, etc see:
* {@link #useFont(File, String, Integer, FontStyle, boolean, Set)}</p>
*
* <p>For gotchas related to font handling please see:
* <a href="https://github.com/danfickle/openhtmltopdf/wiki/Fonts">Wiki: Fonts</a></p>
*
* @param supplier
* @param fontFamily
* @param fontWeight
* @param fontStyle
* @param subset
* @return
* @return this for method chaining
*/
public TFinalClass useFont(FSSupplier<InputStream> supplier, String fontFamily, Integer fontWeight,
FontStyle fontStyle, boolean subset) {
state._fonts.add(new AddedFont(supplier, null, fontWeight, fontFamily, subset, fontStyle));
state._fonts.add(new AddedFont(supplier, null, fontWeight, fontFamily, subset, fontStyle, EnumSet.of(FSFontUseCase.DOCUMENT)));
return (TFinalClass) this;
}

/**
* Simpler overload for
* {@link #useFont(FSSupplier, String, Integer, FontStyle, boolean)}
*
* @param supplier
* @param fontFamily
* @return
* @return this for method chaining
*/
public TFinalClass useFont(FSSupplier<InputStream> supplier, String fontFamily) {
return this.useFont(supplier, fontFamily, 400, FontStyle.NORMAL, true);
Expand All @@ -533,4 +571,14 @@ public enum PageSizeUnits {
public enum FontStyle {
NORMAL, ITALIC, OBLIQUE
}

/**
* Use cases for fonts.
*/
public enum FSFontUseCase {
/** Main document (PDF or Java2D) */
DOCUMENT,
SVG,
MATHML
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@
<html>
<head>
<style>
@font-face {
src: url(fonts/Karla-Bold.ttf);
font-family: 'MyFont';
font-weight: normal;
}
@page {
size: 500px 1500px;
margin: 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<html>
<head>
<style>
@page {
size: 300px 300px;
}
svg {
border: 1px solid red;
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<text y="40" x="10" font-family="Karla" font-weight="bold" font-size="32">Karla Bold</text>
<circle cx="50" cy="80" r="20"/>
</svg>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
package com.openhtmltopdf.visualregressiontests;

import java.awt.FontFormatException;
import java.io.*;

import static org.junit.Assert.assertTrue;

import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.EnumSet;

import com.openhtmltopdf.bidi.support.ICUBidiReorderer;
import com.openhtmltopdf.bidi.support.ICUBidiSplitter;
import com.openhtmltopdf.extend.FSStream;
import com.openhtmltopdf.objects.zxing.ZXingObjectDrawer;
import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder.FSFontUseCase;
import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder.FontStyle;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import com.openhtmltopdf.latexsupport.LaTeXDOMMutator;
Expand All @@ -27,7 +33,12 @@

public class VisualRegressionTest {
private VisualTester vt;


@BeforeClass
public static void configureTests() throws IOException {
TestSupport.makeFontFiles();
}

@Before
public void configureTester() {
File outputDirectory = new File("target/test/visual-tests/test-output/");
Expand Down Expand Up @@ -831,6 +842,9 @@ public void testReplacedSizingSvgNonCss() throws IOException {
public void testReplacedSizingMathMl() throws IOException {
assertTrue(vt.runTest("replaced-sizing-mathml", (builder) -> {
builder.useMathMLDrawer(new MathMLDrawer());
builder.useFont(
new File("target/test/visual-tests/Karla-Bold.ttf"),
"MyFont", 400, FontStyle.NORMAL, true, EnumSet.of(FSFontUseCase.MATHML));
}));
}

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


/**
* Tests that a font can be added as a file for use with SVGs.
*/
@Test
public void testSVGFontFileAddition() throws IOException, FontFormatException {
assertTrue(vt.runTest("svg-font-file-addition",
builder -> {
TestSupport.WITH_SVG.configure(builder);
builder.useFont(
new File("target/test/visual-tests/Karla-Bold.ttf"),
"Karla",
700, FontStyle.NORMAL, true,
EnumSet.of(FSFontUseCase.SVG));
}));
}

// TODO:
// + Elements that appear just on generated overflow pages.
// + content property (page counters, etc)
Expand Down
Loading