/*
 * 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: Java2DPainter.java 1827168 2018-03-19 08:49:57Z ssteiner $ */

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.BorderPainter;
import org.apache.fop.render.intermediate.GraphicsPainter;
import org.apache.fop.render.intermediate.IFContext;
import org.apache.fop.render.intermediate.IFDocumentHandler;
import org.apache.fop.render.intermediate.IFException;
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;

IFPainter implementation that paints on a Graphics2D instance.
/** * {@link org.apache.fop.render.intermediate.IFPainter} implementation that paints on a Graphics2D * instance. */
public class Java2DPainter extends AbstractIFPainter<IFDocumentHandler> {
the IF context
/** the IF context */
protected IFContext ifContext;
The font information
/** The font information */
protected FontInfo fontInfo; private final GraphicsPainter graphicsPainter; private final BorderPainter borderPainter;
The current state, holds a Graphics2D and its context
/** The current state, holds a Graphics2D and its context */
protected Java2DGraphicsState g2dState; private Stack<Java2DGraphicsState> g2dStateStack = new Stack<Java2DGraphicsState>();
Main constructor.
Params:
  • g2d – the target Graphics2D instance
  • context – the IF context
  • fontInfo – the font information
/** * 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, new Java2DDocumentHandler()); } public Java2DPainter(Graphics2D g2d, IFContext context, FontInfo fontInfo, IFDocumentHandler documentHandler) { this(g2d, context, fontInfo, null, documentHandler); }
Special constructor for embedded use (when another painter uses Java2DPainter to convert part of a document into a bitmap, for example).
Params:
  • g2d – the target Graphics2D instance
  • context – the IF context
  • fontInfo – the font information
  • state – the IF state object
/** * 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) { this(g2d, context, fontInfo, state, new Java2DDocumentHandler()); } public Java2DPainter(Graphics2D g2d, IFContext context, FontInfo fontInfo, IFState state, IFDocumentHandler documentHandler) { super(documentHandler); 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()); graphicsPainter = new Java2DGraphicsPainter(this); this.borderPainter = new BorderPainter(graphicsPainter); }
{@inheritDoc}
/** {@inheritDoc} */
public IFContext getContext() { return this.ifContext; }
Returns the associated FontInfo object.
Returns:the font info
/** * Returns the associated {@link FontInfo} object. * @return the font info */
protected FontInfo getFontInfo() { return this.fontInfo; }
Returns the Java2D graphics state.
Returns:the graphics state
/** * Returns the Java2D graphics state. * @return the graphics state */
protected Java2DGraphicsState getState() { return this.g2dState; } //----------------------------------------------------------------------------------------------
{@inheritDoc}
/** {@inheritDoc} */
public void startViewport(AffineTransform transform, Dimension size, Rectangle clipRect) throws IFException { saveGraphicsState(); try { concatenateTransformationMatrix(transform); if (clipRect != null) { clipRect(clipRect); } } catch (IOException ioe) { throw new IFException("I/O error in startViewport()", ioe); } }
{@inheritDoc}
/** {@inheritDoc} */
public void endViewport() throws IFException { restoreGraphicsState(); }
{@inheritDoc}
/** {@inheritDoc} */
public void startGroup(AffineTransform transform, String layer) throws IFException { saveGraphicsState(); try { concatenateTransformationMatrix(transform); } catch (IOException ioe) { throw new IFException("I/O error in startGroup()", ioe); } }
{@inheritDoc}
/** {@inheritDoc} */
public void endGroup() throws IFException { restoreGraphicsState(); }
{@inheritDoc}
/** {@inheritDoc} */
public void drawImage(String uri, Rectangle rect) throws IFException { drawImageUsingURI(uri, rect); }
{@inheritDoc}
/** {@inheritDoc} */
protected RenderingContext createRenderingContext() { Java2DRenderingContext java2dContext = new Java2DRenderingContext( getUserAgent(), g2dState.getGraph(), getFontInfo()); return java2dContext; }
{@inheritDoc}
/** {@inheritDoc} */
public void drawImage(Document doc, Rectangle rect) throws IFException { drawImageUsingDocument(doc, rect); }
{@inheritDoc}
/** {@inheritDoc} */
public void clipRect(Rectangle rect) throws IFException { getState().updateClip(rect); }
{@inheritDoc}
/** {@inheritDoc} */
public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter, BorderProps bpsStart, BorderProps bpsEnd) throws IFException { // TODO Auto-generated method stub }
{@inheritDoc}
/** {@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}
/** {@inheritDoc} */
public void drawBorderRect(Rectangle rect, BorderProps top, BorderProps bottom, BorderProps left, BorderProps right) throws IFException { if (top != null || bottom != null || left != null || right != null) { this.borderPainter.drawBorders(rect, top, bottom, left, right, null); } }
{@inheritDoc}
/** {@inheritDoc} */
public void drawLine(Point start, Point end, int width, Color color, RuleStyle style) throws IFException { try { this.graphicsPainter.drawLine(start, end, width, color, style); } catch (IOException ioe) { throw new IFException("Unexpected error drawing line", ioe); } }
{@inheritDoc}
/** {@inheritDoc} */
public void drawText(int x, int y, int letterSpacing, int wordSpacing, int[][] dp, 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 = Java2DUtil.createGlyphVector(text, g2d, font, fontInfo); Point2D cursor = new Point2D.Float(0, 0); int l = text.length(); if (dp != null && dp[0] != null && (dp[0][0] != 0 || dp[0][1] != 0)) { cursor.setLocation(cursor.getX() + dp[0][0], cursor.getY() - dp[0][1]); gv.setGlyphPosition(0, cursor); } int currentIdx = 0; for (int i = 0; i < l; i++) { int orgChar = text.codePointAt(i); // The dp (GPOS/kerning adjustment) is performed over glyphs and not // characters (GlyphMapping.processWordMapping). The length of dp is // adjusted later to fit the length of the String adding trailing 0. // This means that it's probably ok to consume one of the 2 surrogate // pairs. i += CharUtilities.incrementIfNonBMP(orgChar); float xGlyphAdjust = 0; float yGlyphAdjust = 0; int cw = font.getCharWidth(orgChar); if ((wordSpacing != 0) && CharUtilities.isAdjustableSpace(orgChar)) { xGlyphAdjust += wordSpacing; } xGlyphAdjust += letterSpacing; if (dp != null && i < dp.length && dp[i] != null) { xGlyphAdjust += dp[i][2] - dp[i][0]; yGlyphAdjust += dp[i][3] - dp[i][1]; } if (dp != null && i < dp.length - 1 && dp[i + 1] != null) { xGlyphAdjust += dp[i + 1][0]; yGlyphAdjust += dp[i + 1][1]; } cursor.setLocation(cursor.getX() + cw + xGlyphAdjust, cursor.getY() - yGlyphAdjust); gv.setGlyphPosition(++currentIdx, cursor); } g2d.drawGlyphVector(gv, x, y); }
Saves the current graphics state on the stack.
/** 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.
/** Restores the last graphics state from the stack. */
protected void restoreGraphicsState() { g2dState.dispose(); g2dState = g2dStateStack.pop(); } private void concatenateTransformationMatrix(AffineTransform transform) throws IOException { g2dState.transform(transform); } }