blob: 61100bf3eb71e7ba6aaa07f86b363792538a04af [file] [log] [blame]
/*
* 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.
*/
package org.apache.fop.render.ps;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.xml.transform.stream.StreamResult;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.verification.VerificationMode;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyDouble;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.apache.xmlgraphics.ps.PSGenerator;
import org.apache.xmlgraphics.ps.dsc.ResourceTracker;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.fo.Constants;
import org.apache.fop.fonts.EmbeddingMode;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.fonts.MultiByteFont;
import org.apache.fop.fonts.Typeface;
import org.apache.fop.fonts.truetype.SVGGlyphData;
import org.apache.fop.render.intermediate.IFContext;
import org.apache.fop.render.intermediate.IFException;
import org.apache.fop.render.intermediate.IFState;
import org.apache.fop.traits.BorderProps;
import org.apache.fop.util.CharUtilities;
public class PSPainterTestCase {
private PSDocumentHandler docHandler;
private PSPainter psPainter;
private PSGenerator gen;
private IFState state;
@Before
public void setup() {
state = IFState.create();
FOUserAgent userAgent = mock(FOUserAgent.class);
when(userAgent.getRendererOptions()).thenReturn(Collections.EMPTY_MAP);
IFContext context = mock(IFContext.class);
when(context.getUserAgent()).thenReturn(userAgent);
docHandler = new PSDocumentHandler(context);
gen = mock(PSGenerator.class);
docHandler.gen = gen;
state = IFState.create();
psPainter = new PSPainter(docHandler, state);
}
@Test
public void testNonZeroFontSize() throws IOException {
testFontSize(6, times(1));
}
@Test
public void testZeroFontSize() throws IOException {
testFontSize(0, never());
}
private void testFontSize(int fontSize, VerificationMode test) throws IOException {
state.setFontSize(fontSize);
try {
psPainter.drawText(10, 10, 2, 2, null, "Test");
} catch (Exception ex) {
//Expected
}
verify(gen, test).useColor(state.getTextColor());
}
@Test
public void testDrawBorderRect() {
// the goal of this test is to check that the drawing of rounded corners in PS calls
// PSGraphicsPaiter.cubicBezierTo(); the check is done by verifying that a curveto command is written
// to the PSGenerator
PSGenerator psGenerator = mock(PSGenerator.class);
when(psGenerator.formatDouble(anyFloat())).thenReturn("20.0"); // simplify!
PSRenderingUtil psRenderingUtil = mock(PSRenderingUtil.class);
PSDocumentHandler psDocumentHandler = mock(PSDocumentHandler.class);
when(psDocumentHandler.getGenerator()).thenReturn(psGenerator);
when(psDocumentHandler.getPSUtil()).thenReturn(psRenderingUtil);
PSPainter psPainter = new PSPainter(psDocumentHandler);
// build rectangle 200 x 50 (points, which are converted to milipoints)
Rectangle rectangle = new Rectangle(0, 0, 200000, 50000);
// build border properties: width 4pt, radius 30pt
BorderProps border = new BorderProps(Constants.EN_SOLID, 4000, 30000, 30000, Color.BLACK,
BorderProps.Mode.SEPARATE);
try {
psPainter.drawBorderRect(rectangle, border, border, border, border, Color.WHITE);
// verify(psGenerator, times(16)).writeln("20.0 20.0 20.0 20.0 20.0 20.0 curveto ");
verify(psGenerator, times(4)).saveGraphicsState();
verify(psGenerator, times(155)).formatDouble(anyDouble());
verify(psGenerator, times(51)).writeln(anyString());
} catch (Exception e) {
fail("something broke...");
}
}
@Test
public void testDrawText() throws IOException {
int fontSize = 12000;
String fontName = "MockFont";
PSGenerator psGenerator = mock(PSGenerator.class);
PSRenderingUtil psRenderingUtil = mock(PSRenderingUtil.class);
PSDocumentHandler psDocumentHandler = mock(PSDocumentHandler.class);
FontInfo fontInfo = mock(FontInfo.class);
PSFontResource psFontResource = mock(PSFontResource.class);
MultiByteFont multiByteFont = mock(MultiByteFont.class);
Font font = mock(Font.class);
when(psDocumentHandler.getGenerator()).thenReturn(psGenerator);
when(psDocumentHandler.getPSUtil()).thenReturn(psRenderingUtil);
when(psDocumentHandler.getFontInfo()).thenReturn(fontInfo);
when(psDocumentHandler.getPSResourceForFontKey(fontName)).thenReturn(psFontResource);
when(fontInfo.getInternalFontKey(any(FontTriplet.class))).thenReturn(fontName);
when(fontInfo.getFontInstance(any(FontTriplet.class), anyInt())).thenReturn(font);
Map<String, Typeface> fonts = new HashMap<String, Typeface>();
fonts.put(fontName, multiByteFont);
when(fontInfo.getFonts()).thenReturn(fonts);
IFState ifState = IFState.create();
ifState.setFontSize(fontSize);
PSPainter psPainter = new PSPainter(psDocumentHandler, ifState);
int x = 100000;
int y = 100000;
int letterSpacing = 0;
int wordSpacing = 0;
int[][] dp = {{100, 100, 0, 0}, null, null, {200, 200, -100, -100}};
double xAsDouble = (x + dp[0][0]) / 1000.0;
double yAsDouble = (y - dp[0][1]) / 1000.0;
when(psGenerator.formatDouble(xAsDouble)).thenReturn("100.100");
when(psGenerator.formatDouble(yAsDouble)).thenReturn("99.900");
//0x48 0x65 0x6C 0x6C 0x6F 0x20 0x4D 0x6F 0x63 0x6B 0x21 0x1F4A9
String text = "Hello Mock!\uD83D\uDCA9";
for (int cp : CharUtilities.codepointsIter(text)) {
when(font.mapCodePoint(cp)).thenReturn(cp);
}
try {
psPainter.drawText(x, y, letterSpacing, wordSpacing, dp, text);
verify(psGenerator).writeln("1 0 0 -1 100.100 99.900 Tm");
verify(psGenerator).writeln("[<0048> [-100 100] <0065006C> [200 -200] <006C> [-300 300] "
+ "<006F0020004D006F0063006B002101F4A9>] TJ");
} catch (Exception e) {
fail("something broke...");
}
}
@Test
public void testOTF() throws IFException, IOException {
FOUserAgent ua = FopFactory.newInstance(new File(".").toURI()).newFOUserAgent();
final IFState state = IFState.create();
PSDocumentHandler dh = new PSDocumentHandler(new IFContext(ua)) {
protected PSFontResource getPSResourceForFontKey(String key) {
return new PSFontResource() {
String getName() {
return state.getFontFamily();
}
void notifyResourceUsageOnPage(ResourceTracker resourceTracker) {
}
};
}
};
FontInfo fi = new FontInfo();
addFont(fi, "OTFFont", true);
addFont(fi, "TTFFont", false);
dh.setFontInfo(fi);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
dh.setResult(new StreamResult(bos));
dh.startDocument();
state.setFontSize(10);
state.setTextColor(Color.BLACK);
state.setFontStyle("");
PSPainter p = new PSPainter(dh, state) {
protected String getFontKey(FontTriplet triplet) throws IFException {
return state.getFontFamily();
}
};
state.setFontFamily("TTFFont");
p.drawText(0, 0, 0, 0, null, "test1");
state.setFontFamily("OTFFont");
p.drawText(0, 0, 0, 0, null, "test2");
p.drawText(0, 0, 0, 0, null, "test3");
state.setFontFamily("TTFFont");
p.drawText(0, 0, 0, 0, null, "test4");
Assert.assertTrue(bos.toString(), bos.toString().endsWith("BT\n"
+ "/TTFFont 0.01 F\n"
+ "1 0 0 -1 0 0 Tm\n"
+ "<00000000000000000000> t\n"
+ "/OTFFont.0 0.01 F\n"
+ "1 0 0 -1 0 0 Tm\n"
+ "<0000000000> t\n"
+ "1 0 0 -1 0 0 Tm\n"
+ "<0000000000> t\n"
+ "/TTFFont 0.01 F\n"
+ "1 0 0 -1 0 0 Tm\n"
+ "<00000000000000000000> t\n"));
}
private void addFont(FontInfo fi, String name, boolean otf) {
fi.addFontProperties(name, name, "", 0);
MultiByteFont mbf = new MultiByteFont(null, EmbeddingMode.AUTO);
mbf.setWidthArray(new int[100]);
mbf.setIsOTFFile(otf);
fi.addMetrics(name, mbf);
}
@Test
public void testSVGFont() throws IFException {
FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());
FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
PSDocumentHandler psDocumentHandler = new PSDocumentHandler(new IFContext(foUserAgent));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
psDocumentHandler.setResult(new StreamResult(bos));
psDocumentHandler.startDocument();
psDocumentHandler.startPage(0, "", "", new Dimension());
FontInfo fi = new FontInfo();
fi.addFontProperties("f1", new FontTriplet("a", "normal", 400));
MultiByteFont font = new MultiByteFont(null, null);
font.setWidthArray(new int[1]);
Map<Integer, SVGGlyphData> svgs = new HashMap<>();
SVGGlyphData svgGlyph = new SVGGlyphData();
svgGlyph.setSVG("<svg xmlns=\"http://www.w3.org/2000/svg\">\n"
+ "<circle cx=\"50\" cy=\"50\" r=\"40\" stroke=\"black\" stroke-width=\"3\" fill=\"red\" />\n"
+ "</svg>");
svgs.put(0, svgGlyph);
font.setSVG(svgs);
font.setBBoxArray(new Rectangle[] {new Rectangle()});
fi.addMetrics("f1", font);
psDocumentHandler.setFontInfo(fi);
PSPainter psPainter = new PSPainter(psDocumentHandler);
psPainter.setFont("a", "normal", 400, null, 12, Color.black);
psPainter.drawText(0, 0, 0, 0, null, "test");
Assert.assertTrue(bos.toString().contains("%FOPBeginSVG"));
Assert.assertTrue(bos.toString().contains("[0.00012 0 0 0.00012 0 0] CT"));
Assert.assertTrue(bos.toString().contains("1 0 0 RC"));
}
}