blob: 39664576829689a5f2faa7f2f92eb5eefa87443a [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.
*/
/* $Id$ */
package org.apache.fop.render.java2d;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.io.IOException;
import java.util.Stack;
import org.w3c.dom.Document;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.render.RenderingContext;
import org.apache.fop.render.intermediate.AbstractIFPainter;
import org.apache.fop.render.intermediate.IFContext;
import org.apache.fop.render.intermediate.IFException;
import org.apache.fop.render.intermediate.IFPainter;
import org.apache.fop.render.intermediate.IFState;
import org.apache.fop.traits.BorderProps;
import org.apache.fop.traits.RuleStyle;
import org.apache.fop.util.CharUtilities;
/**
* {@link IFPainter} implementation that paints on a Graphics2D instance.
*/
public class Java2DPainter extends AbstractIFPainter {
/** the IF context */
protected IFContext ifContext;
/** The font information */
protected FontInfo fontInfo;
private Java2DBorderPainter borderPainter;
/** The current state, holds a Graphics2D and its context */
protected Java2DGraphicsState g2dState;
private Stack g2dStateStack = new Stack();
/**
* Main constructor.
* @param g2d the target Graphics2D instance
* @param context the IF context
* @param fontInfo the font information
*/
public Java2DPainter(Graphics2D g2d, IFContext context, FontInfo fontInfo) {
this(g2d, context, fontInfo, null);
}
/**
* Special constructor for embedded use (when another painter uses Java2DPainter
* to convert part of a document into a bitmap, for example).
* @param g2d the target Graphics2D instance
* @param context the IF context
* @param fontInfo the font information
* @param state the IF state object
*/
public Java2DPainter(Graphics2D g2d, IFContext context, FontInfo fontInfo, IFState state) {
super();
this.ifContext = context;
if (state != null) {
this.state = state.push();
} else {
this.state = IFState.create();
}
this.fontInfo = fontInfo;
this.g2dState = new Java2DGraphicsState(g2d, fontInfo, g2d.getTransform());
this.borderPainter = new Java2DBorderPainter(this);
}
/** {@inheritDoc} */
public IFContext getContext() {
return this.ifContext;
}
/**
* Returns the associated {@link FontInfo} object.
* @return the font info
*/
protected FontInfo getFontInfo() {
return this.fontInfo;
}
/**
* Returns the Java2D graphics state.
* @return the graphics state
*/
protected Java2DGraphicsState getState() {
return this.g2dState;
}
//----------------------------------------------------------------------------------------------
/** {@inheritDoc} */
public void startViewport(AffineTransform transform, Dimension size, Rectangle clipRect)
throws IFException {
saveGraphicsState();
try {
concatenateTransformationMatrix(transform);
clipRect(clipRect);
} catch (IOException ioe) {
throw new IFException("I/O error in startViewport()", ioe);
}
}
/** {@inheritDoc} */
public void endViewport() throws IFException {
restoreGraphicsState();
}
/** {@inheritDoc} */
public void startGroup(AffineTransform transform) throws IFException {
saveGraphicsState();
try {
concatenateTransformationMatrix(transform);
} catch (IOException ioe) {
throw new IFException("I/O error in startGroup()", ioe);
}
}
/** {@inheritDoc} */
public void endGroup() throws IFException {
restoreGraphicsState();
}
/** {@inheritDoc} */
public void drawImage(String uri, Rectangle rect) throws IFException {
drawImageUsingURI(uri, rect);
}
/** {@inheritDoc} */
protected RenderingContext createRenderingContext() {
Java2DRenderingContext java2dContext = new Java2DRenderingContext(
getUserAgent(), g2dState.getGraph(), getFontInfo());
return java2dContext;
}
/** {@inheritDoc} */
public void drawImage(Document doc, Rectangle rect) throws IFException {
drawImageUsingDocument(doc, rect);
}
/** {@inheritDoc} */
public void clipRect(Rectangle rect) throws IFException {
getState().updateClip(rect);
}
/** {@inheritDoc} */
public void fillRect(Rectangle rect, Paint fill) throws IFException {
if (fill == null) {
return;
}
if (rect.width != 0 && rect.height != 0) {
g2dState.updatePaint(fill);
g2dState.getGraph().fill(rect);
}
}
/** {@inheritDoc} */
public void drawBorderRect(Rectangle rect, BorderProps before, BorderProps after,
BorderProps start, BorderProps end) throws IFException {
if (before != null || after != null || start != null || end != null) {
try {
this.borderPainter.drawBorders(rect, before, after, start, end);
} catch (IOException e) {
//Won't happen with Java2D
throw new IllegalStateException("Unexpected I/O error");
}
}
}
/** {@inheritDoc} */
public void drawLine(Point start, Point end, int width, Color color, RuleStyle style)
throws IFException {
this.borderPainter.drawLine(start, end, width, color, style);
}
/** {@inheritDoc} */
public void drawText(int x, int y, int letterSpacing, int wordSpacing, int[] dx, String text)
throws IFException {
g2dState.updateColor(state.getTextColor());
FontTriplet triplet = new FontTriplet(
state.getFontFamily(), state.getFontStyle(), state.getFontWeight());
//TODO Ignored: state.getFontVariant()
//TODO Opportunity for font caching if font state is more heavily used
Font font = getFontInfo().getFontInstance(triplet, state.getFontSize());
//String fontName = font.getFontName();
//float fontSize = state.getFontSize() / 1000f;
g2dState.updateFont(font.getFontName(), state.getFontSize() * 1000);
Graphics2D g2d = this.g2dState.getGraph();
GlyphVector gv = g2d.getFont().createGlyphVector(g2d.getFontRenderContext(), text);
Point2D cursor = new Point2D.Float(0, 0);
int l = text.length();
int dxl = (dx != null ? dx.length : 0);
if (dx != null && dxl > 0 && dx[0] != 0) {
cursor.setLocation(cursor.getX() - (dx[0] / 10f), cursor.getY());
gv.setGlyphPosition(0, cursor);
}
for (int i = 0; i < l; i++) {
char orgChar = text.charAt(i);
float glyphAdjust = 0;
int cw = font.getCharWidth(orgChar);
if ((wordSpacing != 0) && CharUtilities.isAdjustableSpace(orgChar)) {
glyphAdjust += wordSpacing;
}
glyphAdjust += letterSpacing;
if (dx != null && i < dxl - 1) {
glyphAdjust += dx[i + 1];
}
cursor.setLocation(cursor.getX() + cw + glyphAdjust, cursor.getY());
gv.setGlyphPosition(i + 1, cursor);
}
g2d.drawGlyphVector(gv, x, y);
}
/** Saves the current graphics state on the stack. */
protected void saveGraphicsState() {
g2dStateStack.push(g2dState);
g2dState = new Java2DGraphicsState(g2dState);
}
/** Restores the last graphics state from the stack. */
protected void restoreGraphicsState() {
g2dState.dispose();
g2dState = (Java2DGraphicsState)g2dStateStack.pop();
}
private void concatenateTransformationMatrix(AffineTransform transform) throws IOException {
g2dState.transform(transform);
}
}