diff options
author | Vincent Hennebert <vhennebert@apache.org> | 2013-07-29 21:45:20 +0000 |
---|---|---|
committer | Vincent Hennebert <vhennebert@apache.org> | 2013-07-29 21:45:20 +0000 |
commit | f8e822efe1de8bd8192dbb8ff035b9a79f876614 (patch) | |
tree | 8d38f873dd101e6ed0c2ba3b4dfa29e1eb0fdc60 /test | |
parent | c0b99ad44d0e1409008886e2f687c46f4ac05d9d (diff) | |
download | xmlgraphics-fop-f8e822efe1de8bd8192dbb8ff035b9a79f876614.tar.gz xmlgraphics-fop-f8e822efe1de8bd8192dbb8ff035b9a79f876614.zip |
Directly use FOP fonts to lay out SVG images for PDF, PS and AFP outputs.
The metrics are now taken from FOP configured fonts and no longer from AWT equivalents. That avoids discrepancies in case AWT and FOP use slightly different fonts, or if the font is not installed on the system. That actually also avoids having to install the font on the system.
FOP is also used for the primary layout of text (prior to SVG-specific transforms like translation or rotation) for consistency between SVG and XSL-FO.
This is a joint work from Peter Hancock and myself.
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_FopFontsForSVG@1508208 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'test')
22 files changed, 1176 insertions, 53 deletions
diff --git a/test/java/org/apache/fop/afp/fonts/IntegerKeyStoreTestCase.java b/test/java/org/apache/fop/afp/fonts/IntegerKeyStoreTestCase.java new file mode 100644 index 000000000..96c0618a7 --- /dev/null +++ b/test/java/org/apache/fop/afp/fonts/IntegerKeyStoreTestCase.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.afp.fonts; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +public class IntegerKeyStoreTestCase { + + @Test + public void getAndPut() { + IntegerKeyStore<Integer> sut = new IntegerKeyStore<Integer>(); + assertNull(sut.get(0)); + sut.put(0, 0); + assertEquals(Integer.valueOf(0), sut.get(0)); + sut.put(0, 1); + assertEquals(Integer.valueOf(1), sut.get(0)); + sut.put(0, null); + assertNull(sut.get(0)); + try { + sut.put(-1, 0); + fail("Negative index"); + } catch (IndexOutOfBoundsException e) { + // As expected + } + } + +} diff --git a/test/java/org/apache/fop/fonts/DejaVuLGCSerifTestCase.java b/test/java/org/apache/fop/fonts/DejaVuLGCSerifTestCase.java index 20212b002..4c89d00da 100644 --- a/test/java/org/apache/fop/fonts/DejaVuLGCSerifTestCase.java +++ b/test/java/org/apache/fop/fonts/DejaVuLGCSerifTestCase.java @@ -24,11 +24,11 @@ import java.io.File; import org.junit.Before; import org.junit.Test; +import static org.junit.Assert.assertEquals; + import org.apache.fop.apps.io.InternalResourceResolver; import org.apache.fop.apps.io.ResourceResolverFactory; -import static org.junit.Assert.assertEquals; - /** * */ @@ -58,4 +58,17 @@ public class DejaVuLGCSerifTestCase { public void testFontName() { assertEquals("DejaVuLGCSerif", font.getFontName()); } + + @Test + public void testUnderline() { + assertEquals(-840, font.getUnderlinePosition(10)); + assertEquals(430, font.getUnderlineThickness(10)); + } + + @Test + public void testStrikeout() { + assertEquals(2340, font.getStrikeoutPosition(10)); + assertEquals(490, font.getStrikeoutThickness(10)); + } + } diff --git a/test/java/org/apache/fop/fonts/FontEventProcessingTestCase.java b/test/java/org/apache/fop/fonts/FontEventProcessingTestCase.java index 509ee56f4..b4271ba60 100644 --- a/test/java/org/apache/fop/fonts/FontEventProcessingTestCase.java +++ b/test/java/org/apache/fop/fonts/FontEventProcessingTestCase.java @@ -52,13 +52,4 @@ public class FontEventProcessingTestCase { MimeConstants.MIME_PDF); } - @Test - public void testSVGFontStrokedAsShapes() throws Exception { - // svg-fonts.fo embeds two fonts; one that is present in the system and the other is not; the - // missing font is stroked as shapes while the fonts that exists is stroked as text - InputStream inStream = getClass().getResourceAsStream("svg-fonts.fo"); - eventsTests.doTest(inStream, null, FontEventProducer.class.getName() + ".svgTextStrokedAsShapes", - MimeConstants.MIME_PDF); - } - } diff --git a/test/java/org/apache/fop/fonts/svg-fonts.fo b/test/java/org/apache/fop/fonts/svg-fonts.fo deleted file mode 100644 index 0c5f3f599..000000000 --- a/test/java/org/apache/fop/fonts/svg-fonts.fo +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" standalone="no"?> -<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg"> - <fo:layout-master-set> - <fo:simple-page-master master-name="page"> - <fo:region-body /> - </fo:simple-page-master> - </fo:layout-master-set> - <fo:page-sequence master-reference="page"> - <fo:flow flow-name="xsl-region-body"> - <fo:block> - <fo:instream-foreign-object> - <svg:svg width="250" height="50"> - <svg:font horiz-adv-x="1000"> - <svg:font-face font-family="Missing" units-per-em="1000" underline-position="-100" - underline-thickness="50" /> - <svg:glyph unicode="A" horiz-adv-x="686" d="M162,186l362,0l78,-186l84,0l-308,708l-70,0l-308,-708l84,0M343,624l153,-372l-307,0z" /> - <svg:glyph unicode="C" horiz-adv-x="704" d="M620,154C567,72 491,48 417,48C235,48 126,191 126,354C126,517 235,660 417,660C492,660 571,613 599,567l63,47C600,693 505,726 417,726C206,726 48,569 48,354C48,139 206,-18 417,-18C534,-18 632,39 679,112z" /> - <svg:glyph unicode="F" horiz-adv-x="556" d="M168,335l330,0l0,66l-330,0l0,241l355,0l0,66l-427,0l0,-708l72,0z" /> - <svg:glyph unicode="G" horiz-adv-x="778" d="M673,631C610,694 529,726 417,726C206,726 48,569 48,354C48,139 206,-18 417,-18C503,-18 606,7 685,54l0,347l-241,0l0,-66l169,0l0,-237C560,68 490,48 417,48C235,48 126,191 126,354C126,517 235,660 417,660C504,660 571,629 619,578z" /> - </svg:font> - <svg:font horiz-adv-x="1000"> - <!-- this is not Helvetica but it is here to increase coverage and show the code takes expected path --> - <svg:font-face font-family="Helvetica" units-per-em="1000" underline-position="-100" - underline-thickness="50" /> - <svg:glyph unicode="A" horiz-adv-x="686" d="M162,186l362,0l78,-186l84,0l-308,708l-70,0l-308,-708l84,0M343,624l153,-372l-307,0z" /> - <svg:glyph unicode="C" horiz-adv-x="704" d="M620,154C567,72 491,48 417,48C235,48 126,191 126,354C126,517 235,660 417,660C492,660 571,613 599,567l63,47C600,693 505,726 417,726C206,726 48,569 48,354C48,139 206,-18 417,-18C534,-18 632,39 679,112z" /> - <svg:glyph unicode="F" horiz-adv-x="556" d="M168,335l330,0l0,66l-330,0l0,241l355,0l0,66l-427,0l0,-708l72,0z" /> - <svg:glyph unicode="G" horiz-adv-x="778" d="M673,631C610,694 529,726 417,726C206,726 48,569 48,354C48,139 206,-18 417,-18C503,-18 606,7 685,54l0,347l-241,0l0,-66l169,0l0,-237C560,68 490,48 417,48C235,48 126,191 126,354C126,517 235,660 417,660C504,660 571,629 619,578z" /> - </svg:font> - <svg:text x="20" y="20" font-family="Missing" font-size="12">ACFG</svg:text> - <svg:text x="20" y="40" font-family="Helvetica" font-size="12">ACFG</svg:text> - </svg:svg> - </fo:instream-foreign-object> - </fo:block> - </fo:flow> - </fo:page-sequence> -</fo:root> diff --git a/test/java/org/apache/fop/fonts/truetype/TTFFileTestCase.java b/test/java/org/apache/fop/fonts/truetype/TTFFileTestCase.java index e04347032..2d583dd3e 100644 --- a/test/java/org/apache/fop/fonts/truetype/TTFFileTestCase.java +++ b/test/java/org/apache/fop/fonts/truetype/TTFFileTestCase.java @@ -26,12 +26,12 @@ import java.util.Map; import org.junit.Test; -import org.apache.fop.fonts.truetype.TTFFile.PostScriptVersion; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import org.apache.fop.fonts.truetype.TTFFile.PostScriptVersion; + /** * Class for testing org.apache.fop.fonts.truetype.TTFFile */ @@ -426,6 +426,24 @@ public class TTFFileTestCase { assertEquals(true, droidmonoTTFFile.isEmbeddable()); } + /** Underline position and thickness. */ + @Test + public void testUnderline() { + assertEquals(-63, dejavuTTFFile.getUnderlinePosition()); + assertEquals(43, dejavuTTFFile.getUnderlineThickness()); + assertEquals(-75, droidmonoTTFFile.getUnderlinePosition()); + assertEquals(49, droidmonoTTFFile.getUnderlineThickness()); + } + + /** Strikeout position and thickness. */ + @Test + public void testStrikeout() { + assertEquals(258, dejavuTTFFile.getStrikeoutPosition()); + assertEquals(49, dejavuTTFFile.getStrikeoutThickness()); + assertEquals(243, droidmonoTTFFile.getStrikeoutPosition()); + assertEquals(49, droidmonoTTFFile.getStrikeoutThickness()); + } + /** * Test readFont() - Add implementation if necessary. */ diff --git a/test/java/org/apache/fop/fonts/type1/AFMParserTestCase.java b/test/java/org/apache/fop/fonts/type1/AFMParserTestCase.java index 93443a0d9..31a613567 100644 --- a/test/java/org/apache/fop/fonts/type1/AFMParserTestCase.java +++ b/test/java/org/apache/fop/fonts/type1/AFMParserTestCase.java @@ -19,9 +19,6 @@ package org.apache.fop.fonts.type1; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import java.awt.Rectangle; import java.io.IOException; import java.io.InputStream; @@ -29,6 +26,9 @@ import java.util.List; import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + /** * Test case for {@link AFMParser}. */ @@ -128,4 +128,13 @@ public class AFMParserTestCase { private boolean objectEquals(Object o1, Object o2) { return o1 == null ? o2 == null : (o1 == o2 || o1.equals(o2)); } + + @Test + public void testUnderlinePositionAndThickness() throws IOException { + AFMFile afm = sut.parse(getClass().getResourceAsStream("underline.afm"), null); + AFMWritingDirectionMetrics metrics = afm.getWritingDirectionMetrics(0); + assertEquals(-96, metrics.getUnderlinePosition()); + assertEquals(58, metrics.getUnderlineThickness()); + } + } diff --git a/test/java/org/apache/fop/fonts/type1/underline.afm b/test/java/org/apache/fop/fonts/type1/underline.afm new file mode 100644 index 000000000..8137b41eb --- /dev/null +++ b/test/java/org/apache/fop/fonts/type1/underline.afm @@ -0,0 +1,4 @@ +StartFontMetrics 2.0 +UnderlinePosition -96 +UnderlineThickness 58 +EndFontMetrics diff --git a/test/java/org/apache/fop/svg/NativeTextPainterTest.java b/test/java/org/apache/fop/svg/NativeTextPainterTest.java new file mode 100644 index 000000000..1c2c3b582 --- /dev/null +++ b/test/java/org/apache/fop/svg/NativeTextPainterTest.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.svg; + +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.io.File; +import java.io.IOException; + +import org.w3c.dom.Document; + +import org.apache.batik.bridge.BridgeContext; +import org.apache.batik.bridge.GVTBuilder; +import org.apache.batik.dom.svg.SAXSVGDocumentFactory; +import org.apache.batik.gvt.GraphicsNode; +import org.apache.batik.gvt.TextPainter; + +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.apps.FopFactory; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.base14.Base14FontCollection; +import org.apache.fop.svg.font.FOPFontFamilyResolverImpl; + +abstract class NativeTextPainterTest { + + protected final void runTest(String testcase, OperatorValidator validator) throws Exception { + FontInfo fontInfo = createFontInfo(); + BridgeContext bridgeContext = createBridgeContext(fontInfo); + GraphicsNode svg = loadSVG(bridgeContext, testcase); + Graphics2D g2d = createGraphics2D(fontInfo, validator); + svg.paint(g2d); + validator.end(); + } + + private FontInfo createFontInfo() { + FontInfo fontInfo = new FontInfo(); + new Base14FontCollection(true).setup(0, fontInfo); + return fontInfo; + } + + private BridgeContext createBridgeContext(FontInfo fontInfo) { + FOUserAgent userAgent = FopFactory.newInstance(new File(".").toURI()).newFOUserAgent(); + SVGUserAgent svgUserAgent = new SVGUserAgent(userAgent, new FOPFontFamilyResolverImpl(fontInfo), + new AffineTransform()); + BridgeContext bridgeContext = new BridgeContext(svgUserAgent); + bridgeContext.setTextPainter(createTextPainter(fontInfo)); + return bridgeContext; + } + + protected abstract TextPainter createTextPainter(FontInfo fontInfo); + + private GraphicsNode loadSVG(BridgeContext bridgeContext, String resourceName) throws IOException { + SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(null); + Document svg = factory.createDocument(null, getClass().getResourceAsStream(resourceName)); + GVTBuilder builder = new GVTBuilder(); + return builder.build(bridgeContext, svg); + } + + protected abstract Graphics2D createGraphics2D(FontInfo fontInfo, OperatorValidator validator); + +} diff --git a/test/java/org/apache/fop/svg/OperatorValidator.java b/test/java/org/apache/fop/svg/OperatorValidator.java new file mode 100644 index 000000000..959adc9a1 --- /dev/null +++ b/test/java/org/apache/fop/svg/OperatorValidator.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.svg; + +import java.util.LinkedList; +import java.util.Queue; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +class OperatorValidator { + + private static interface Match { + + boolean match(String line); + } + + private static class MatchSequence implements OperatorValidator.Match { + + private final Queue<OperatorValidator.Match> expectedMatches = new LinkedList<OperatorValidator.Match>(); + + private OperatorValidator.Match currentMatch; + + private static final OperatorValidator.Match FINAL_MATCH = new Match() { + + public boolean match(String line) { + return false; + } + }; + + public boolean isExhausted() { + return currentMatch == FINAL_MATCH; + } + + public void addMatch(OperatorValidator.Match match) { + if (currentMatch == null) { + currentMatch = match; + } else { + expectedMatches.add(match); + } + } + + public boolean match(String line) { + boolean match = currentMatch.match(line); + if (match) { + if(expectedMatches.isEmpty()) { + currentMatch = FINAL_MATCH; + } else { + currentMatch = expectedMatches.remove(); + } + } + return match; + } + } + + private static class OperatorMatch implements OperatorValidator.Match { + + final String operator; + + final String line; + + OperatorMatch(String operator, String line) { + this.operator = operator; + this.line = line; + } + + public boolean match(String line) { + if (line.contains(operator)) { + assertEquals(this.line, line); + return true; + } + return false; + } + } + + private final OperatorValidator.MatchSequence matchSequence = new MatchSequence(); + + public OperatorValidator addOperatorMatch(String operator, String expectedLine) { + matchSequence.addMatch(new OperatorMatch(operator, expectedLine)); + return this; + } + + public void check(String line) { + matchSequence.match(line); + } + + public void end() { + assertTrue("Expected operators remain", matchSequence.isExhausted()); + } + +}
\ No newline at end of file diff --git a/test/java/org/apache/fop/svg/PDFTextPainterTestCase.java b/test/java/org/apache/fop/svg/PDFTextPainterTestCase.java new file mode 100644 index 000000000..52f18bb5e --- /dev/null +++ b/test/java/org/apache/fop/svg/PDFTextPainterTestCase.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.svg; + +import java.awt.Graphics2D; +import java.io.StringWriter; + +import org.junit.Test; + +import org.apache.batik.gvt.TextPainter; + +import org.apache.xmlgraphics.java2d.GraphicContext; + +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.pdf.PDFDocument; +import org.apache.fop.svg.font.FOPFontFamilyResolverImpl; + +public class PDFTextPainterTestCase extends NativeTextPainterTest { + + private static class OperatorCheckingPDFGraphics2D extends PDFGraphics2D { + + OperatorCheckingPDFGraphics2D(FontInfo fontInfo, final OperatorValidator validator) { + super(false, fontInfo, new PDFDocument("test"), null, null, null, 0); + this.currentStream = new StringWriter() { + + @Override + public void write(String str) { + validator.check(str); + } + + }; + } + } + + @Override + protected TextPainter createTextPainter(FontInfo fontInfo) { + return new PDFTextPainter(fontInfo); + } + + @Override + protected Graphics2D createGraphics2D(FontInfo fontInfo, OperatorValidator validator) { + PDFGraphics2D g2d = new OperatorCheckingPDFGraphics2D(fontInfo, validator); + g2d.setGraphicContext(new GraphicContext()); + return g2d; + } + + @Test + public void testRotatedGlyph() throws Exception { + runTest("rotated-glyph.svg", new OperatorValidator() + .addOperatorMatch("Tm", "1 0 0 -1 40 110 Tm ") + .addOperatorMatch("TJ", "[(A)] TJ\n") + .addOperatorMatch("Tm", "0.70710677 0.7071068 0.7071068 -0.70710677 106.69999695 110 Tm ") + .addOperatorMatch("TJ", "[(B)] TJ\n") + .addOperatorMatch("Tm", "1 0 0 -1 173.3999939 110 Tm ") + .addOperatorMatch("TJ", "[(C)] TJ\n")); + } + + @Test + public void testDxDy() throws Exception { + runTest("dx-dy.svg", new OperatorValidator() + .addOperatorMatch("Tm", "1 0 0 -1 55 35 Tm ") + .addOperatorMatch("TJ", "[(ABCDE)] TJ\n") + .addOperatorMatch("Tm", "1 0 0 -1 55 75 Tm ") + .addOperatorMatch("TJ", "[(A)] TJ\n") + .addOperatorMatch("Tm", "1 0 0 -1 69 85 Tm ") + .addOperatorMatch("TJ", "[(B)] TJ\n") + .addOperatorMatch("Tm", "1 0 0 -1 109 80 Tm ") + .addOperatorMatch("TJ", "[(C)] TJ\n") + .addOperatorMatch("Tm", "1 0 0 -1 91 65 Tm ") + .addOperatorMatch("TJ", "[(D)] TJ\n") + .addOperatorMatch("Tm", "1 0 0 -1 127 75 Tm ") + .addOperatorMatch("TJ", "[(E)] TJ\n")); + } + + @Test + public void testSpacing() throws Exception { + runTest("spacing.svg", new OperatorValidator() + .addOperatorMatch("Tm", "1 0 0 -1 0 0 Tm ") + .addOperatorMatch("TJ", "[(V) 80 (A) 70 (V)] TJ\n") + .addOperatorMatch("Tm", "1 0 0 -1 0 0 Tm ") + .addOperatorMatch("TJ", "[(V) 80 (A) 70 (V)] TJ\n") + .addOperatorMatch("Tm", "1 0 0 -1 0 0 Tm ") + .addOperatorMatch("TJ", "[(V) -20 (A) -30 (V)] TJ\n") + .addOperatorMatch("Tm", "1 0 0 -1 0 0 Tm ") + .addOperatorMatch("TJ", "[(ab) -111 ( ) -389 (cd)] TJ\n")); + } + + @Test + public void testGlyphOrientation() throws Exception { + runTest("glyph-orientation.svg", new OperatorValidator() + .addOperatorMatch("Tm", "0 1 1 0 738.5 0 Tm ") + .addOperatorMatch("TJ", "[(A)] TJ\n") + .addOperatorMatch("Tm", "0 1 1 0 738.5 667 Tm ") + .addOperatorMatch("TJ", "[(B)] TJ\n") + .addOperatorMatch("Tm", "0 1 1 0 738.5 1334 Tm ") + .addOperatorMatch("TJ", "[(C)] TJ\n") + .addOperatorMatch("Tm", "0 1 1 0 738.5 2056 Tm ") + .addOperatorMatch("TJ", "[(D)] TJ\n") + .addOperatorMatch("Tm", "1 0 0 -1 2149 718 Tm ") + .addOperatorMatch("TJ", "[(E)] TJ\n") + .addOperatorMatch("Tm", "1 0 0 -1 2165.5 1643 Tm ") + .addOperatorMatch("TJ", "[(F)] TJ\n") + .addOperatorMatch("Tm", "1 0 0 -1 2124 2568 Tm ") + .addOperatorMatch("TJ", "[(G)] TJ\n") + .addOperatorMatch("Tm", "1 0 0 -1 2138.5 3493 Tm ") + .addOperatorMatch("TJ", "[(H)] TJ\n") + .addOperatorMatch("Tm", "0 -1 -1 0 718 5000 Tm ") + .addOperatorMatch("TJ", "[(I)] TJ\n") + .addOperatorMatch("Tm", "0 -1 -1 0 1643 5000 Tm ") + .addOperatorMatch("TJ", "[(J)] TJ\n") + .addOperatorMatch("Tm", "0 -1 -1 0 2568 5000 Tm ") + .addOperatorMatch("TJ", "[(K)] TJ\n") + .addOperatorMatch("Tm", "0 -1 -1 0 3493 5000 Tm ") + .addOperatorMatch("TJ", "[(L)] TJ\n")); + } + + @Test + public void testBaselineShift() throws Exception { + runTest("baseline-shift.svg", new OperatorValidator() + .addOperatorMatch("Tm", "1 0 0 -1 0 0 Tm ") + .addOperatorMatch("TJ", "[(AB)] TJ\n") + .addOperatorMatch("Tm", "1 0 0 -1 1334 -462.5 Tm ") + .addOperatorMatch("TJ", "[(CD)] TJ\n") + .addOperatorMatch("Tm", "1 0 0 -1 2778 0 Tm ") + .addOperatorMatch("TJ", "[(EF)] TJ\n") + .addOperatorMatch("Tm", "1 0 0 -1 4056 462.5 Tm ") + .addOperatorMatch("TJ", "[(GH)] TJ\n") + .addOperatorMatch("Tm", "1 0 0 -1 5556 0 Tm ") + .addOperatorMatch("TJ", "[(IJ)] TJ\n")); + } + +} diff --git a/test/java/org/apache/fop/svg/PSTextPainterTestCase.java b/test/java/org/apache/fop/svg/PSTextPainterTestCase.java new file mode 100644 index 000000000..2d5de7455 --- /dev/null +++ b/test/java/org/apache/fop/svg/PSTextPainterTestCase.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.svg; + +import java.awt.Graphics2D; +import java.io.IOException; + +import org.junit.Test; + +import org.apache.commons.io.output.NullOutputStream; + +import org.apache.batik.gvt.TextPainter; + +import org.apache.xmlgraphics.java2d.GraphicContext; +import org.apache.xmlgraphics.java2d.ps.PSGraphics2D; +import org.apache.xmlgraphics.ps.PSGenerator; + +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.render.ps.PSTextPainter; + +public class PSTextPainterTestCase extends NativeTextPainterTest { + + private static class OperatorCheckingPSGraphics2D extends PSGraphics2D { + + OperatorCheckingPSGraphics2D(FontInfo fontInfo, final OperatorValidator validator) { + super(false, new PSGenerator(new NullOutputStream()) { + + @Override + public void writeln(String cmd) throws IOException { + validator.check(cmd); + } + + }); + } + } + + @Override + protected TextPainter createTextPainter(FontInfo fontInfo) { + return new PSTextPainter(fontInfo); + } + + @Override + protected Graphics2D createGraphics2D(FontInfo fontInfo, OperatorValidator validator) { + PSGraphics2D g2d = new OperatorCheckingPSGraphics2D(fontInfo, validator); + g2d.setGraphicContext(new GraphicContext()); + return g2d; + } + + @Test + public void testRotatedGlyph() throws Exception { + runTest("rotated-glyph.svg", new OperatorValidator() + .addOperatorMatch("Tm", "1 0 0 -1 40 110 Tm") + .addOperatorMatch("xshow", "(A)\n[0] xshow") + .addOperatorMatch("Tm", "0.70711 0.70711 0.70711 -0.70711 106.7 110 Tm") + .addOperatorMatch("xshow", "(B)\n[0] xshow") + .addOperatorMatch("Tm", "1 0 0 -1 173.39999 110 Tm") + .addOperatorMatch("xshow", "(C)\n[0] xshow")); + } + +} diff --git a/test/java/org/apache/fop/svg/baseline-shift.svg b/test/java/org/apache/fop/svg/baseline-shift.svg new file mode 100644 index 000000000..0f375b9af --- /dev/null +++ b/test/java/org/apache/fop/svg/baseline-shift.svg @@ -0,0 +1,9 @@ +<?xml version="1.0"?> +<svg width="150" height="60" xmlns="http://www.w3.org/2000/svg"> +<rect x="0" y="0" width="100%" height="100%" fill="none" stroke="black" stroke-width="2"/> +<g transform="translate(10, 40) scale(20) scale(0.001)"> +<text font-family="sans-serif" font-size="1000"> +AB<tspan baseline-shift="super">CD</tspan>EF<tspan baseline-shift="sub">GH</tspan>IJ +</text> +</g> +</svg> diff --git a/test/java/org/apache/fop/svg/dx-dy.svg b/test/java/org/apache/fop/svg/dx-dy.svg new file mode 100644 index 000000000..cfdc2de01 --- /dev/null +++ b/test/java/org/apache/fop/svg/dx-dy.svg @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<svg width="200" height="100" xmlns="http://www.w3.org/2000/svg"> +<rect x="0" y="0" width="100%" height="100%" stroke="black" stroke-width="2" fill="none"/> +<g font-family="monospace" font-size="30"> + <text x="55" y="35">ABCDE</text> + <text x="55" y="75" dx="0 -4 22 -36 18" dy="0 10 -5 -15 10">ABCDE</text> +</g> +</svg> diff --git a/test/java/org/apache/fop/svg/font/BasicGlyphVectorTestCase.java b/test/java/org/apache/fop/svg/font/BasicGlyphVectorTestCase.java new file mode 100644 index 000000000..c6f062a84 --- /dev/null +++ b/test/java/org/apache/fop/svg/font/BasicGlyphVectorTestCase.java @@ -0,0 +1,193 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.svg.font; + +import java.awt.Rectangle; +import java.awt.font.GlyphMetrics; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.batik.gvt.font.GVTGlyphMetrics; +import org.apache.batik.gvt.font.GVTLineMetrics; + +import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontMetrics; + +/** + * Tests all the methods of {@link FOPGVTGlyphVector} with a mocked font. + */ +public class BasicGlyphVectorTestCase extends FOPGVTGlyphVectorTest { + + private final int fontSize = 10000; + + @Before + public void createGlyphVector() { + FontMetrics metrics = mockFontMetrics(); + Font font = mockFont(metrics); + FOPGVTFont gvtFont = mockGVTFont(font); + CharacterIterator it = new StringCharacterIterator("ABC"); + glyphVector = new FOPGVTGlyphVector(gvtFont, it, null); + glyphVector.performDefaultLayout(); + } + + private FontMetrics mockFontMetrics() { + FontMetrics metrics = mock(FontMetrics.class); + when(metrics.getAscender(eq(fontSize))).thenReturn(8000000); + when(metrics.getDescender(eq(fontSize))).thenReturn(-4000000); + when(metrics.getWidth(eq(1), eq(fontSize))).thenReturn(10000000); + when(metrics.getBoundingBox(eq(1), eq(fontSize))).thenReturn( + new Rectangle(-1000000, -2000000, 3000000, 4000000)); + when(metrics.getWidth(eq(2), eq(fontSize))).thenReturn(11000000); + when(metrics.getBoundingBox(eq(2), eq(fontSize))).thenReturn( + new Rectangle(-5000000, -6000000, 7000000, 9000000)); + when(metrics.getWidth(eq(3), eq(fontSize))).thenReturn(12000000); + when(metrics.getBoundingBox(eq(3), eq(fontSize))).thenReturn( + new Rectangle(-9000000, -10000000, 11000000, 14000000)); + return metrics; + } + + private Font mockFont(FontMetrics metrics) { + Font font = mock(Font.class); + when(font.getFontMetrics()).thenReturn(metrics); + when(font.getFontSize()).thenReturn(fontSize); + when(font.mapChar(eq('A'))).thenReturn((char) 1); + when(font.mapChar(eq('B'))).thenReturn((char) 2); + when(font.mapChar(eq('C'))).thenReturn((char) 3); + return font; + } + + private FOPGVTFont mockGVTFont(Font font) { + FOPGVTFont gvtFont = mock(FOPGVTFont.class); + when(gvtFont.getFont()).thenReturn(font); + when(gvtFont.getLineMetrics(anyInt())).thenReturn( + new GVTLineMetrics(8, 0, null, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + return gvtFont; + } + + @Test + public void getGlyphCodeReturnsGlyphIndex() { + assertEquals(1, glyphVector.getGlyphCode(0)); + assertEquals(2, glyphVector.getGlyphCode(1)); + assertEquals(3, glyphVector.getGlyphCode(2)); + } + + @Test + public void testGetGlyphCodes() { + assertArrayEquals(new int[] {1, 2, 3}, glyphVector.getGlyphCodes(0, 3, null)); + assertArrayEquals(new int[] {2, 3}, glyphVector.getGlyphCodes(1, 2, null)); + } + + @Test + public void testGetGlyphMetrics() { + assertGlyphMetricsEqual(new GVTGlyphMetrics(10, 12, new Rectangle(-1, -2, 3, 4), GlyphMetrics.STANDARD), + glyphVector.getGlyphMetrics(0)); + assertGlyphMetricsEqual(new GVTGlyphMetrics(11, 12, new Rectangle(-5, -3, 7, 9), GlyphMetrics.STANDARD), + glyphVector.getGlyphMetrics(1)); + assertGlyphMetricsEqual(new GVTGlyphMetrics(12, 12, new Rectangle(-9, -4, 11, 14), GlyphMetrics.STANDARD), + glyphVector.getGlyphMetrics(2)); + } + + private void assertGlyphMetricsEqual(GVTGlyphMetrics expected, GVTGlyphMetrics actual) { + assertEquals(expected.getHorizontalAdvance(), actual.getHorizontalAdvance(), 0); + assertEquals(expected.getVerticalAdvance(), actual.getVerticalAdvance(), 0); + assertEquals(expected.getBounds2D(), actual.getBounds2D()); + assertEquals(expected.getLSB(), actual.getLSB(), 0); + assertEquals(expected.getRSB(), actual.getRSB(), 0); + assertEquals(expected.getType(), actual.getType()); + assertEquals(expected.isCombining(), actual.isCombining()); + assertEquals(expected.isComponent(), actual.isComponent()); + assertEquals(expected.isLigature(), actual.isLigature()); + assertEquals(expected.isStandard(), actual.isStandard()); + assertEquals(expected.isWhitespace(), actual.isWhitespace()); + } + + @Test + public void testGetGlyphPosition() { + assertEquals(new Point2D.Float(0, 0), glyphVector.getGlyphPosition(0)); + assertEquals(new Point2D.Float(10, 0), glyphVector.getGlyphPosition(1)); + assertEquals(new Point2D.Float(21, 0), glyphVector.getGlyphPosition(2)); + assertEquals(new Point2D.Float(33, 0), glyphVector.getGlyphPosition(3)); + } + + @Test + public void testGetGlyphPositions() { + float[] expectedPositions = new float[] {0, 0, 10, 0, 21, 0, 33, 0}; + assertArrayEquals(expectedPositions, glyphVector.getGlyphPositions(0, 4, null), 0); + assertArrayEquals(expectedPositions, glyphVector.getGlyphPositions(0, 4, new float[8]), 0); + } + + @Test + public void testGetGlyphOutline() { + assertEquals(new Rectangle(-1, -2, 3, 4), glyphVector.getGlyphOutline(0).getBounds()); + assertEquals(new Rectangle(5, -3, 7, 9), glyphVector.getGlyphOutline(1).getBounds()); + assertEquals(new Rectangle(12, -4, 11, 14), glyphVector.getGlyphOutline(2).getBounds()); + } + + @Test + public void testGetOutline() { + assertEquals(new Rectangle(-1, -4, 24, 14), glyphVector.getOutline().getBounds()); + } + + @Test + public void testGetLogicalBounds() { + assertEquals(new Rectangle(0, -8, 33, 12), glyphVector.getLogicalBounds()); + } + + @Test + public void testGetLogicalBoundsRotated() { + for (int i = 0; i < 3; i++) { + glyphVector.setGlyphTransform(i, new AffineTransform(0.7, 0.7, -0.7, 0.7, 0, 0)); + } + assertEquals(new Rectangle2D.Float(-2.8f, -5.6f, 37.8f, 16.8f), glyphVector.getLogicalBounds()); + } + + @Test + public void testGetBounds() { + assertEquals(new Rectangle(-1, -4, 24, 14), glyphVector.getBounds2D(null)); + } + + @Test + public void testGetGlyphVisualBounds() { + assertEquals(new Rectangle(-1, -2, 3, 4), glyphVector.getGlyphVisualBounds(0).getBounds()); + assertEquals(new Rectangle(5, -3, 7, 9), glyphVector.getGlyphVisualBounds(1).getBounds()); + assertEquals(new Rectangle(12, -4, 11, 14), glyphVector.getGlyphVisualBounds(2).getBounds()); + } + + @Test + public void testGetGlyphLogicalBounds() { + assertEquals(new Rectangle(0, -8, 10, 12), glyphVector.getGlyphLogicalBounds(0).getBounds()); + assertEquals(new Rectangle(10, -8, 11, 12), glyphVector.getGlyphLogicalBounds(1).getBounds()); + assertEquals(new Rectangle(21, -8, 12, 12), glyphVector.getGlyphLogicalBounds(2).getBounds()); + } + +} diff --git a/test/java/org/apache/fop/svg/font/FOPFontFamilyResolverTestCase.java b/test/java/org/apache/fop/svg/font/FOPFontFamilyResolverTestCase.java new file mode 100644 index 000000000..2f5668cbc --- /dev/null +++ b/test/java/org/apache/fop/svg/font/FOPFontFamilyResolverTestCase.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.svg.font; + +import java.awt.FontFormatException; +import java.awt.GraphicsEnvironment; +import java.awt.font.FontRenderContext; +import java.awt.font.LineMetrics; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.Collections; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + +import org.apache.batik.gvt.font.GVTFontFamily; +import org.apache.batik.gvt.font.GVTLineMetrics; + +import org.apache.fop.fonts.FontInfo; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class FOPFontFamilyResolverTestCase { + + private static FontInfo fontInfo; + + private FOPFontFamilyResolver resolver; + + @BeforeClass + public static void setUpFontInfo() { + fontInfo = new FontInfoBuilder() + .useDejaVuLGCSerif() + .useDroidSansMono() + .build(); + } + + @Before + public void createFontFamilyResolver() { + resolver = new FOPFontFamilyResolverImpl(fontInfo); + } + + @Test + public void testResolve() { + assertNull(resolver.resolve("Unavailable")); + assertNotNull(resolver.resolve(FontInfoBuilder.DEJAVU_LGC_SERIF)); + } + + @Test + public void testGetFamilyThatCanDisplay() { + GVTFontFamily family = resolver.getFamilyThatCanDisplay('\u0180'); + assertEquals(FontInfoBuilder.DEJAVU_LGC_SERIF, family.getFamilyName()); + family = resolver.getFamilyThatCanDisplay('\u02F3'); + assertEquals(FontInfoBuilder.DROID_SANS_MONO, family.getFamilyName()); + family = resolver.getFamilyThatCanDisplay('\u02DF'); + assertNull(family); + } + + @Test + public void testDeriveFont() { + FOPGVTFontFamily family = (FOPGVTFontFamily) resolver.resolve(FontInfoBuilder.DEJAVU_LGC_SERIF); + FOPGVTFont font = family.deriveFont(10, Collections.emptyMap()); + assertEquals(10, font.getSize(), 0); + assertTrue(font.canDisplay('\u01F6')); + assertFalse(font.canDisplay('\u01F7')); + } + + @Test + @Ignore("FOP metrics don't match AWT, but not sure who is right and who is wrong") + public void testLineMetrics() throws FontFormatException, IOException { + FOPGVTFontFamily family = (FOPGVTFontFamily) resolver.resolve(FontInfoBuilder.DEJAVU_LGC_SERIF); + FOPGVTFont font = family.deriveFont(10, Collections.emptyMap()); + GVTLineMetrics fopMetrics = font.getLineMetrics("", null); + LineMetrics awtMetrics = getAWTLineMetrics(); + printDifference("Ascent", awtMetrics.getAscent(), fopMetrics.getAscent()); + printDifference("Descent", awtMetrics.getDescent(), fopMetrics.getDescent()); + printDifference("Height", awtMetrics.getHeight(), fopMetrics.getHeight()); + printDifference("Leading", awtMetrics.getLeading(), fopMetrics.getLeading()); + printDifference("StrikethroughOffset", awtMetrics.getStrikethroughOffset(), + fopMetrics.getStrikethroughOffset()); + printDifference("StrikethroughThickness", awtMetrics.getStrikethroughThickness(), + fopMetrics.getStrikethroughThickness()); + printDifference("UnderlineOffset", awtMetrics.getUnderlineOffset(), + fopMetrics.getUnderlineOffset()); + printDifference("UnderlineThickness", awtMetrics.getUnderlineThickness(), + fopMetrics.getUnderlineThickness()); + } + + private LineMetrics getAWTLineMetrics() throws FontFormatException, IOException { + File fontFile = new File("test/resources/fonts/ttf/DejaVuLGCSerif.ttf"); + java.awt.Font awtFont = java.awt.Font.createFont(java.awt.Font.TRUETYPE_FONT, fontFile).deriveFont(10f); + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + BufferedImage dummyImage = new BufferedImage(1000, 1000, BufferedImage.TYPE_INT_RGB); + FontRenderContext frc = ge.createGraphics(dummyImage).getFontRenderContext(); + LineMetrics awtMetrics = awtFont.getLineMetrics("ABC", frc); + return awtMetrics; + } + + private void printDifference(String value, float awt, float fop) { + System.out.println(String.format("%22s AWT: %10f FOP: %10f Difference: %.2f%%", value, awt, fop, + (fop - awt) / awt * 100)); + } + +} diff --git a/test/java/org/apache/fop/svg/font/FOPGVTFontTestCase.java b/test/java/org/apache/fop/svg/font/FOPGVTFontTestCase.java new file mode 100644 index 000000000..c7af068ce --- /dev/null +++ b/test/java/org/apache/fop/svg/font/FOPGVTFontTestCase.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.svg.font; + +import java.text.StringCharacterIterator; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.fop.fonts.Font; +import org.apache.fop.svg.font.FOPGVTFont; + +public class FOPGVTFontTestCase { + + private FOPGVTFont font; + + @Before + public void createFont() { + Font f = mock(Font.class); + when(f.hasChar(eq((char) 0))).thenReturn(false); + when(f.hasChar(eq((char) 1))).thenReturn(true); + font = new FOPGVTFont(f, null); + } + + @Test + public void testCanDisplayUpTo() { + char[] text = new char[] {1, 1, 1}; + testCanDisplayUpToVariants(text, -1, 0, 3); + testCanDisplayUpToVariants(text, -1, 1, 3); + text = new char[] {1, 1, 0, 1}; + testCanDisplayUpToVariants(text, 2, 0, 4); + testCanDisplayUpToVariants(text, 2, 1, 4); + testCanDisplayUpToVariants(text, 2, 2, 4); + testCanDisplayUpToVariants(text, -1, 3, 4); + testCanDisplayUpToVariants(text, -1, 1, 2); + } + + @Test + public void testCanDisplayUpToString() { + assertEquals(-1, font.canDisplayUpTo(new String(new char[] {1, 1, 1}))); + assertEquals(0, font.canDisplayUpTo(new String(new char[] {0, 1, 1}))); + assertEquals(1, font.canDisplayUpTo(new String(new char[] {1, 0, 1}))); + assertEquals(2, font.canDisplayUpTo(new String(new char[] {1, 1, 0}))); + } + + private void testCanDisplayUpToVariants(char[] text, int expected, int start, int limit) { + assertEquals(expected, font.canDisplayUpTo(text, start, limit)); + assertEquals(expected, font.canDisplayUpTo(new StringCharacterIterator(new String(text)), start, limit)); + } +} diff --git a/test/java/org/apache/fop/svg/font/FOPGVTGlyphVectorTest.java b/test/java/org/apache/fop/svg/font/FOPGVTGlyphVectorTest.java new file mode 100644 index 000000000..0995ab4df --- /dev/null +++ b/test/java/org/apache/fop/svg/font/FOPGVTGlyphVectorTest.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.svg.font; + + +public abstract class FOPGVTGlyphVectorTest { + + protected FOPGVTGlyphVector glyphVector; + +} diff --git a/test/java/org/apache/fop/svg/font/FontInfoBuilder.java b/test/java/org/apache/fop/svg/font/FontInfoBuilder.java new file mode 100644 index 000000000..f7a5825bd --- /dev/null +++ b/test/java/org/apache/fop/svg/font/FontInfoBuilder.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.svg.font; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.apache.fop.apps.io.InternalResourceResolver; +import org.apache.fop.apps.io.ResourceResolverFactory; +import org.apache.fop.fonts.EmbeddingMode; +import org.apache.fop.fonts.EncodingMode; +import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontMetrics; +import org.apache.fop.fonts.truetype.TTFFontLoader; + +class FontInfoBuilder { + + public static final String DEJAVU_LGC_SERIF = "DejaVu LGC Serif"; + + public static final String DROID_SANS_MONO = "Droid Sans Mono"; + + private static final boolean USE_ADVANCED_BY_DEFAULT = true; + + private FontInfo fontInfo; + + private int fontKey; + + public FontInfoBuilder() { + reset(); + } + + private void reset() { + fontInfo = new FontInfo(); + fontKey = 1; + } + + public FontInfoBuilder useDejaVuLGCSerif() { + return useDejaVuLGCSerif(USE_ADVANCED_BY_DEFAULT); + } + + public FontInfoBuilder useDejaVuLGCSerif(boolean useAdvanced) { + try { + return useFont(DEJAVU_LGC_SERIF, "DejaVuLGCSerif.ttf", useAdvanced); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public FontInfoBuilder useDroidSansMono() { + return useDroidSansMono(USE_ADVANCED_BY_DEFAULT); + } + + public FontInfoBuilder useDroidSansMono(boolean useAdvanced) { + try { + return useFont(DROID_SANS_MONO, "DroidSansMono.ttf", useAdvanced); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private FontInfoBuilder useFont(String fontName, String filename, boolean useAdvanced) + throws IOException, URISyntaxException { + URI baseURI = new File("test/resources/fonts/ttf").toURI(); + InternalResourceResolver resolver = ResourceResolverFactory.createDefaultInternalResourceResolver(baseURI); + TTFFontLoader fontLoader = new TTFFontLoader(new URI(filename), null, true, + EmbeddingMode.AUTO, EncodingMode.AUTO, true, useAdvanced, resolver); + FontMetrics font = fontLoader.getFont(); + registerFont(font, "F" + fontKey++, fontName); + return this; + } + + private void registerFont(FontMetrics font, String key, String familyName) { + fontInfo.addMetrics(key, font); + fontInfo.addFontProperties(key, familyName, Font.STYLE_NORMAL, Font.WEIGHT_NORMAL); + } + + public FontInfo build() { + FontInfo fontInfo = this.fontInfo; + reset(); + return fontInfo; + } +} diff --git a/test/java/org/apache/fop/svg/font/GlyphLayoutTestCase.java b/test/java/org/apache/fop/svg/font/GlyphLayoutTestCase.java new file mode 100644 index 000000000..34cba604a --- /dev/null +++ b/test/java/org/apache/fop/svg/font/GlyphLayoutTestCase.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.svg.font; + +import java.util.Collections; + +import org.junit.Test; + +import org.apache.fop.fonts.FontInfo; + +import static org.junit.Assert.assertEquals; + +/** + * Specifically tests glyph positioning from a real font. + */ +public class GlyphLayoutTestCase extends FOPGVTGlyphVectorTest { + + /** + * Glyph positioning using the legacy kern table. + */ + @Test + public void testBasicGlyphPositioning() throws Exception { + testGlyphLayout(false); + } + + /** + * Glyph positioning using GPOS sub-tables. + */ + @Test + public void testAdvancedGlyphPositioning() throws Exception { + testGlyphLayout(true); + } + + private void testGlyphLayout(boolean useAdvanced) { + FOPGVTFont font = loadFont(useAdvanced); + glyphVector = (FOPGVTGlyphVector) font.createGlyphVector(null, "L\u201DP,V.F,A\u2019LT."); + glyphVector.performDefaultLayout(); + // Values in font units (unitsPerEm = 2048), glyph width - kern + int[] widths = { + /* L */ 1360 - 491, + /* " */ 1047, + /* P */ 1378 - 415, + /* , */ 651, + /* V */ 1479 - 358, + /* . */ 651, + /* F */ 1421 - 319, + /* , */ 651, + /* A */ 1479 - 301, + /* ' */ 651, + /* L */ 1360 - 167, + /* T */ 1366 - 301, + /* . */ 651}; + checkGlyphPositions(13, widths); + } + + private FOPGVTFont loadFont(boolean useAdvanced) { + FontInfo fontInfo = new FontInfoBuilder().useDejaVuLGCSerif(useAdvanced).build(); + FOPFontFamilyResolver resolver = new FOPFontFamilyResolverImpl(fontInfo); + FOPGVTFontFamily family = resolver.resolve(FontInfoBuilder.DEJAVU_LGC_SERIF); + return family.deriveFont(1000, Collections.emptyMap()); + } + + private void checkGlyphPositions(int expectedGlyphCount, int[] widths) { + assertEquals(expectedGlyphCount, glyphVector.getNumGlyphs()); + float[] positions = new float[2 * (widths.length + 1)]; + for (int i = 0, n = 2; i < widths.length; i++, n += 2) { + positions[n] = positions[n - 2] + widths[i] / 2.048f; + } + for (int i = 0; i <= widths.length; i++) { + assertEquals(positions[2 * i], glyphVector.getGlyphPosition(i).getX(), 3); + } + } + +} diff --git a/test/java/org/apache/fop/svg/glyph-orientation.svg b/test/java/org/apache/fop/svg/glyph-orientation.svg new file mode 100644 index 000000000..4900a3b02 --- /dev/null +++ b/test/java/org/apache/fop/svg/glyph-orientation.svg @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<svg width="100" height="140" xmlns="http://www.w3.org/2000/svg"> +<rect x="0" y="0" width="100%" height="100%" fill="none" stroke="black" stroke-width="2"/> +<g transform="translate(10) scale(20) scale(0.001) translate(0, 1000)" + font-family="sans-serif" font-size="1000"> + <text x="1000" writing-mode="tb">ABCD</text> + <text x="2500" writing-mode="tb" glyph-orientation-vertical="0">EFGH</text> + <text x="0" y="5000" glyph-orientation-horizontal="270">IJKL</text> +</g> +</svg> diff --git a/test/java/org/apache/fop/svg/rotated-glyph.svg b/test/java/org/apache/fop/svg/rotated-glyph.svg new file mode 100644 index 000000000..8b942905e --- /dev/null +++ b/test/java/org/apache/fop/svg/rotated-glyph.svg @@ -0,0 +1,5 @@ +<?xml version="1.0"?> +<svg width="300" height="150" xmlns="http://www.w3.org/2000/svg"> +<rect x="0" y="0" width="100%" height="100%" fill="none" stroke="black" stroke-width="2"/> +<text font-family="Helvetica" font-size="100" x="40" y="110" rotate="0 45 0">ABC</text> +</svg> diff --git a/test/java/org/apache/fop/svg/spacing.svg b/test/java/org/apache/fop/svg/spacing.svg new file mode 100644 index 000000000..943ab1d04 --- /dev/null +++ b/test/java/org/apache/fop/svg/spacing.svg @@ -0,0 +1,21 @@ +<?xml version="1.0"?> +<svg width="150" height="200" xmlns="http://www.w3.org/2000/svg"> +<rect x="0" y="0" width="100%" height="100%" stroke="black" stroke-width="2" fill="none"/> +<g transform="translate(10) scale(40) scale(0.001) translate(0, 1000)" + font-family="sans-serif" font-size="1000"> + <g transform=""> + <text>VAV</text> + <line x1="667" y1="-818" x2="667" y2="100" stroke-width="10" stroke="blue"/> + </g> + <g transform="translate(0, 1000)"> + <text kerning="0">VAV</text> + <line x1="667" y1="-818" x2="667" y2="100" stroke-width="10" stroke="blue"/> + </g> + <g transform="translate(0, 2000)"> + <text letter-spacing="100">VAV</text> + </g> + <g transform="translate(0, 3000)"> + <text word-spacing="500">ab cd</text> + </g> +</g> +</svg> |