summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/java/org/apache/fop/afp/fonts/IntegerKeyStoreTestCase.java48
-rw-r--r--test/java/org/apache/fop/fonts/DejaVuLGCSerifTestCase.java17
-rw-r--r--test/java/org/apache/fop/fonts/FontEventProcessingTestCase.java9
-rw-r--r--test/java/org/apache/fop/fonts/svg-fonts.fo37
-rw-r--r--test/java/org/apache/fop/fonts/truetype/TTFFileTestCase.java22
-rw-r--r--test/java/org/apache/fop/fonts/type1/AFMParserTestCase.java15
-rw-r--r--test/java/org/apache/fop/fonts/type1/underline.afm4
-rw-r--r--test/java/org/apache/fop/svg/NativeTextPainterTest.java78
-rw-r--r--test/java/org/apache/fop/svg/OperatorValidator.java108
-rw-r--r--test/java/org/apache/fop/svg/PDFTextPainterTestCase.java149
-rw-r--r--test/java/org/apache/fop/svg/PSTextPainterTestCase.java77
-rw-r--r--test/java/org/apache/fop/svg/baseline-shift.svg9
-rw-r--r--test/java/org/apache/fop/svg/dx-dy.svg8
-rw-r--r--test/java/org/apache/fop/svg/font/BasicGlyphVectorTestCase.java193
-rw-r--r--test/java/org/apache/fop/svg/font/FOPFontFamilyResolverTestCase.java127
-rw-r--r--test/java/org/apache/fop/svg/font/FOPGVTFontTestCase.java72
-rw-r--r--test/java/org/apache/fop/svg/font/FOPGVTGlyphVectorTest.java27
-rw-r--r--test/java/org/apache/fop/svg/font/FontInfoBuilder.java102
-rw-r--r--test/java/org/apache/fop/svg/font/GlyphLayoutTestCase.java91
-rw-r--r--test/java/org/apache/fop/svg/glyph-orientation.svg10
-rw-r--r--test/java/org/apache/fop/svg/rotated-glyph.svg5
-rw-r--r--test/java/org/apache/fop/svg/spacing.svg21
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>