| /* |
| * 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.pcl; |
| |
| 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.geom.AffineTransform; |
| import java.awt.geom.Point2D; |
| import java.awt.geom.Rectangle2D; |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Stack; |
| |
| import org.w3c.dom.Document; |
| |
| import org.apache.xmlgraphics.image.loader.ImageException; |
| import org.apache.xmlgraphics.image.loader.ImageInfo; |
| import org.apache.xmlgraphics.image.loader.ImageProcessingHints; |
| import org.apache.xmlgraphics.image.loader.ImageSize; |
| import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D; |
| import org.apache.xmlgraphics.java2d.GraphicContext; |
| import org.apache.xmlgraphics.java2d.Graphics2DImagePainter; |
| |
| import org.apache.fop.fonts.CIDFontType; |
| import org.apache.fop.fonts.Font; |
| import org.apache.fop.fonts.FontTriplet; |
| import org.apache.fop.fonts.FontType; |
| import org.apache.fop.fonts.LazyFont; |
| import org.apache.fop.fonts.MultiByteFont; |
| import org.apache.fop.fonts.Typeface; |
| import org.apache.fop.render.ImageHandlerUtil; |
| import org.apache.fop.render.RenderingContext; |
| import org.apache.fop.render.intermediate.AbstractIFPainter; |
| import org.apache.fop.render.intermediate.IFException; |
| import org.apache.fop.render.intermediate.IFState; |
| import org.apache.fop.render.intermediate.IFUtil; |
| import org.apache.fop.render.java2d.CustomFontMetricsMapper; |
| import org.apache.fop.render.java2d.FontMetricsMapper; |
| import org.apache.fop.render.java2d.Java2DPainter; |
| import org.apache.fop.render.pcl.fonts.PCLCharacterWriter; |
| import org.apache.fop.render.pcl.fonts.PCLSoftFont; |
| import org.apache.fop.render.pcl.fonts.PCLSoftFontManager; |
| import org.apache.fop.render.pcl.fonts.PCLSoftFontManager.PCLTextSegment; |
| import org.apache.fop.render.pcl.fonts.truetype.PCLTTFCharacterWriter; |
| import org.apache.fop.traits.BorderProps; |
| import org.apache.fop.traits.RuleStyle; |
| import org.apache.fop.util.CharUtilities; |
| |
| /** |
| * {@link org.apache.fop.render.intermediate.IFPainter} implementation that produces PCL 5. |
| */ |
| public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements PCLConstants { |
| |
| private static final boolean DEBUG = false; |
| |
| /** The PCL generator */ |
| private PCLGenerator gen; |
| |
| private PCLPageDefinition currentPageDefinition; |
| private int currentPrintDirection; |
| //private GeneralPath currentPath = null; |
| |
| private Stack<GraphicContext> graphicContextStack = new Stack<GraphicContext>(); |
| private GraphicContext graphicContext = new GraphicContext(); |
| private PCLSoftFontManager sfManager; |
| |
| /** |
| * Main constructor. |
| * @param parent the parent document handler |
| * @param pageDefinition the page definition describing the page to be rendered |
| */ |
| public PCLPainter(PCLDocumentHandler parent, PCLPageDefinition pageDefinition) { |
| super(parent); |
| this.gen = parent.getPCLGenerator(); |
| this.state = IFState.create(); |
| this.currentPageDefinition = pageDefinition; |
| } |
| |
| PCLRenderingUtil getPCLUtil() { |
| return getDocumentHandler().getPCLUtil(); |
| } |
| |
| /** @return the target resolution */ |
| protected int getResolution() { |
| int resolution = Math.round(getUserAgent().getTargetResolution()); |
| if (resolution <= 300) { |
| return 300; |
| } else { |
| return 600; |
| } |
| } |
| |
| private boolean isSpeedOptimized() { |
| return getPCLUtil().getRenderingMode() == PCLRenderingMode.SPEED; |
| } |
| |
| //---------------------------------------------------------------------------------------------- |
| |
| /** {@inheritDoc} */ |
| public void startViewport(AffineTransform transform, Dimension size, Rectangle clipRect) |
| throws IFException { |
| saveGraphicsState(); |
| try { |
| concatenateTransformationMatrix(transform); |
| /* PCL cannot clip! |
| if (clipRect != null) { |
| 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, String layer) 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() { |
| PCLRenderingContext pdfContext = new PCLRenderingContext( |
| getUserAgent(), this.gen, getPCLUtil()) { |
| |
| public Point2D transformedPoint(int x, int y) { |
| return PCLPainter.this.transformedPoint(x, y); |
| } |
| |
| public GraphicContext getGraphicContext() { |
| return PCLPainter.this.graphicContext; |
| } |
| |
| }; |
| return pdfContext; |
| } |
| |
| /** {@inheritDoc} */ |
| public void drawImage(Document doc, Rectangle rect) throws IFException { |
| drawImageUsingDocument(doc, rect); |
| } |
| |
| /** {@inheritDoc} */ |
| public void clipRect(Rectangle rect) throws IFException { |
| //PCL cannot clip (only HP GL/2 can) |
| //If you need clipping support, switch to RenderingMode.BITMAP. |
| } |
| |
| /** {@inheritDoc} */ |
| public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter, |
| BorderProps bpsStart, BorderProps bpsEnd) throws IFException { |
| //PCL cannot clip (only HP GL/2 can) |
| //If you need clipping support, switch to RenderingMode.BITMAP. |
| |
| } |
| |
| /** {@inheritDoc} */ |
| public void fillRect(Rectangle rect, Paint fill) throws IFException { |
| if (fill == null) { |
| return; |
| } |
| if (rect.width != 0 && rect.height != 0) { |
| Color fillColor = null; |
| if (fill != null) { |
| if (fill instanceof Color) { |
| fillColor = (Color)fill; |
| } else { |
| throw new UnsupportedOperationException("Non-Color paints NYI"); |
| } |
| try { |
| setCursorPos(rect.x, rect.y); |
| gen.fillRect(rect.width, rect.height, fillColor, getPCLUtil().isColorEnabled()); |
| } catch (IOException ioe) { |
| throw new IFException("I/O error in fillRect()", ioe); |
| } |
| } |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| public void drawBorderRect(final Rectangle rect, |
| final BorderProps top, final BorderProps bottom, |
| final BorderProps left, final BorderProps right) throws IFException { |
| if (isSpeedOptimized()) { |
| super.drawBorderRect(rect, top, bottom, left, right, null); |
| return; |
| } |
| if (top != null || bottom != null || left != null || right != null) { |
| final Rectangle boundingBox = rect; |
| final Dimension dim = boundingBox.getSize(); |
| |
| Graphics2DImagePainter painter = new Graphics2DImagePainter() { |
| |
| public void paint(Graphics2D g2d, Rectangle2D area) { |
| g2d.translate(-rect.x, -rect.y); |
| |
| Java2DPainter painter = new Java2DPainter(g2d, |
| getContext(), getFontInfo(), state); |
| try { |
| painter.drawBorderRect(rect, top, bottom, left, right); |
| } catch (IFException e) { |
| //This should never happen with the Java2DPainter |
| throw new RuntimeException("Unexpected error while painting borders", e); |
| } |
| } |
| |
| public Dimension getImageSize() { |
| return dim.getSize(); |
| } |
| |
| }; |
| paintMarksAsBitmap(painter, boundingBox); |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| public void drawLine(final Point start, final Point end, |
| final int width, final Color color, final RuleStyle style) |
| throws IFException { |
| if (isSpeedOptimized()) { |
| super.drawLine(start, end, width, color, style); |
| return; |
| } |
| final Rectangle boundingBox = getLineBoundingBox(start, end, width); |
| final Dimension dim = boundingBox.getSize(); |
| |
| Graphics2DImagePainter painter = new Graphics2DImagePainter() { |
| |
| public void paint(Graphics2D g2d, Rectangle2D area) { |
| g2d.translate(-boundingBox.x, -boundingBox.y); |
| |
| Java2DPainter painter = new Java2DPainter(g2d, |
| getContext(), getFontInfo(), state); |
| try { |
| painter.drawLine(start, end, width, color, style); |
| } catch (IFException e) { |
| //This should never happen with the Java2DPainter |
| throw new RuntimeException("Unexpected error while painting a line", e); |
| } |
| } |
| |
| public Dimension getImageSize() { |
| return dim.getSize(); |
| } |
| |
| }; |
| paintMarksAsBitmap(painter, boundingBox); |
| } |
| |
| private void paintMarksAsBitmap(Graphics2DImagePainter painter, Rectangle boundingBox) |
| throws IFException { |
| ImageInfo info = new ImageInfo(null, null); |
| ImageSize size = new ImageSize(); |
| size.setSizeInMillipoints(boundingBox.width, boundingBox.height); |
| info.setSize(size); |
| ImageGraphics2D img = new ImageGraphics2D(info, painter); |
| |
| Map hints = new java.util.HashMap(); |
| if (isSpeedOptimized()) { |
| //Gray text may not be painted in this case! We don't get dithering in Sun JREs. |
| //But this approach is about twice as fast as the grayscale image. |
| hints.put(ImageProcessingHints.BITMAP_TYPE_INTENT, |
| ImageProcessingHints.BITMAP_TYPE_INTENT_MONO); |
| } else { |
| hints.put(ImageProcessingHints.BITMAP_TYPE_INTENT, |
| ImageProcessingHints.BITMAP_TYPE_INTENT_GRAY); |
| } |
| hints.put(ImageHandlerUtil.CONVERSION_MODE, ImageHandlerUtil.CONVERSION_MODE_BITMAP); |
| PCLRenderingContext context = (PCLRenderingContext)createRenderingContext(); |
| context.setSourceTransparencyEnabled(true); |
| try { |
| drawImage(img, boundingBox, context, true, hints); |
| } catch (IOException ioe) { |
| throw new IFException( |
| "I/O error while painting marks using a bitmap", ioe); |
| } catch (ImageException ie) { |
| throw new IFException( |
| "Error while painting marks using a bitmap", ie); |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| public void drawText(int x, int y, int letterSpacing, int wordSpacing, int[][] dp, String text) |
| throws IFException { |
| try { |
| 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 |
| String fontKey = getFontKey(triplet); |
| Typeface tf = getTypeface(fontKey); |
| boolean drawAsBitmaps = getPCLUtil().isAllTextAsBitmaps(); |
| boolean pclFont = HardcodedFonts.setFont(gen, fontKey, state.getFontSize(), text); |
| if (pclFont) { |
| drawTextNative(x, y, letterSpacing, wordSpacing, dp, text, triplet); |
| } else { |
| // TrueType conversion to a soft font (PCL 5 Technical Reference - Chapter 11) |
| if (!drawAsBitmaps && isTrueType(tf)) { |
| if (sfManager == null) { |
| sfManager = new PCLSoftFontManager(gen.fontReaderMap); |
| } |
| if (getPCLUtil().isOptimizeResources() || sfManager.getSoftFont(tf, text) == null) { |
| for (char c : text.toCharArray()) { |
| tf.mapChar(c); |
| } |
| ByteArrayOutputStream baos = sfManager.makeSoftFont(tf, text); |
| if (baos != null) { |
| if (getPCLUtil().isOptimizeResources()) { |
| gen.addFont(sfManager, tf); |
| } else { |
| gen.writeBytes(baos.toByteArray()); |
| } |
| } |
| } |
| String formattedSize = gen.formatDouble2(state.getFontSize() / 1000.0); |
| gen.writeCommand(String.format("(s%sV", formattedSize)); |
| List<PCLTextSegment> textSegments = sfManager.getTextSegments(text, tf); |
| if (textSegments.isEmpty()) { |
| textSegments.add(new PCLTextSegment(sfManager.getSoftFontID(tf), text)); |
| } |
| boolean first = true; |
| for (PCLTextSegment textSegment : textSegments) { |
| gen.writeCommand(String.format("(%dX", textSegment.getFontID())); |
| PCLSoftFont softFont = sfManager.getSoftFontFromID(textSegment.getFontID()); |
| PCLCharacterWriter charWriter = new PCLTTFCharacterWriter(softFont); |
| gen.writeBytes(sfManager.assignFontID(textSegment.getFontID())); |
| gen.writeBytes(charWriter.writeCharacterDefinitions(textSegment.getText())); |
| if (first) { |
| drawTextUsingSoftFont(x, y, letterSpacing, wordSpacing, dp, |
| textSegment.getText(), triplet, softFont); |
| first = false; |
| } else { |
| drawTextUsingSoftFont(-1, -1, letterSpacing, wordSpacing, dp, |
| textSegment.getText(), triplet, softFont); |
| } |
| } |
| } else { |
| drawTextAsBitmap(x, y, letterSpacing, wordSpacing, dp, text, triplet); |
| if (DEBUG) { |
| state.setTextColor(Color.GRAY); |
| HardcodedFonts.setFont(gen, "F1", state.getFontSize(), text); |
| drawTextNative(x, y, letterSpacing, wordSpacing, dp, text, triplet); |
| } |
| } |
| } |
| } catch (IOException ioe) { |
| throw new IFException("I/O error in drawText()", ioe); |
| } |
| } |
| |
| private boolean isTrueType(Typeface tf) { |
| if (tf.getFontType().equals(FontType.TRUETYPE)) { |
| return true; |
| } else if (tf instanceof CustomFontMetricsMapper) { |
| Typeface realFont = ((CustomFontMetricsMapper) tf).getRealFont(); |
| if (realFont instanceof MultiByteFont) { |
| return ((MultiByteFont) realFont).getCIDType().equals(CIDFontType.CIDTYPE2); |
| } |
| } |
| return false; |
| } |
| |
| private Typeface getTypeface(String fontName) { |
| if (fontName == null) { |
| throw new NullPointerException("fontName must not be null"); |
| } |
| Typeface tf = getFontInfo().getFonts().get(fontName); |
| if (tf instanceof LazyFont) { |
| tf = ((LazyFont)tf).getRealFont(); |
| } |
| return tf; |
| } |
| |
| private void drawTextNative(int x, int y, int letterSpacing, int wordSpacing, int[][] dp, |
| String text, FontTriplet triplet) throws IOException { |
| Color textColor = state.getTextColor(); |
| if (textColor != null) { |
| gen.setTransparencyMode(true, false); |
| if (getDocumentHandler().getPCLUtil().isColorEnabled()) { |
| gen.selectColor(textColor); |
| } else { |
| gen.selectGrayscale(textColor); |
| } |
| } |
| |
| gen.setTransparencyMode(true, true); |
| setCursorPos(x, y); |
| |
| float fontSize = state.getFontSize() / 1000f; |
| Font font = getFontInfo().getFontInstance(triplet, state.getFontSize()); |
| int l = text.length(); |
| |
| StringBuffer sb = new StringBuffer(Math.max(16, l)); |
| if (dp != null && dp[0] != null && dp[0][0] != 0) { |
| if (dp[0][0] > 0) { |
| sb.append("\u001B&a+").append(gen.formatDouble2(dp[0][0] / 100.0)).append('H'); |
| } else { |
| sb.append("\u001B&a-").append(gen.formatDouble2(-dp[0][0] / 100.0)).append('H'); |
| } |
| } |
| if (dp != null && dp[0] != null && dp[0][1] != 0) { |
| if (dp[0][1] > 0) { |
| sb.append("\u001B&a-").append(gen.formatDouble2(dp[0][1] / 100.0)).append('V'); |
| } else { |
| sb.append("\u001B&a+").append(gen.formatDouble2(-dp[0][1] / 100.0)).append('V'); |
| } |
| } |
| for (int i = 0; i < l; i++) { |
| char orgChar = text.charAt(i); |
| char ch; |
| float xGlyphAdjust = 0; |
| float yGlyphAdjust = 0; |
| if (font.hasChar(orgChar)) { |
| ch = font.mapChar(orgChar); |
| } else { |
| if (CharUtilities.isFixedWidthSpace(orgChar)) { |
| //Fixed width space are rendered as spaces so copy/paste works in a reader |
| ch = font.mapChar(CharUtilities.SPACE); |
| int spaceDiff = font.getCharWidth(ch) - font.getCharWidth(orgChar); |
| xGlyphAdjust = -(10 * spaceDiff / fontSize); |
| } else { |
| ch = font.mapChar(orgChar); |
| } |
| } |
| sb.append(ch); |
| |
| 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]; |
| } |
| |
| if (xGlyphAdjust != 0) { |
| if (xGlyphAdjust > 0) { |
| sb.append("\u001B&a+").append(gen.formatDouble2(xGlyphAdjust / 100.0)).append('H'); |
| } else { |
| sb.append("\u001B&a-").append(gen.formatDouble2(-xGlyphAdjust / 100.0)).append('H'); |
| } |
| } |
| if (yGlyphAdjust != 0) { |
| if (yGlyphAdjust > 0) { |
| sb.append("\u001B&a-").append(gen.formatDouble2(yGlyphAdjust / 100.0)).append('V'); |
| } else { |
| sb.append("\u001B&a+").append(gen.formatDouble2(-yGlyphAdjust / 100.0)).append('V'); |
| } |
| } |
| |
| } |
| gen.getOutputStream().write(sb.toString().getBytes(gen.getTextEncoding())); |
| |
| } |
| |
| private void drawTextUsingSoftFont(int x, int y, int letterSpacing, int wordSpacing, int[][] dp, |
| String text, FontTriplet triplet, PCLSoftFont softFont) throws IOException { |
| Color textColor = state.getTextColor(); |
| if (textColor != null) { |
| gen.setTransparencyMode(true, false); |
| if (getDocumentHandler().getPCLUtil().isColorEnabled()) { |
| gen.selectColor(textColor); |
| } else { |
| gen.selectGrayscale(textColor); |
| } |
| } |
| |
| if (x != -1 && y != -1) { |
| setCursorPos(x, y); |
| } |
| |
| float fontSize = state.getFontSize() / 1000f; |
| Font font = getFontInfo().getFontInstance(triplet, state.getFontSize()); |
| int l = text.length(); |
| int[] dx = IFUtil.convertDPToDX(dp); |
| int dxl = (dx != null ? dx.length : 0); |
| |
| StringBuffer sb = new StringBuffer(Math.max(16, l)); |
| if (dx != null && dxl > 0 && dx[0] != 0) { |
| sb.append("\u001B&a+").append(gen.formatDouble2(dx[0] / 100.0)).append('H'); |
| } |
| String current = ""; |
| for (int i = 0; i < l; i++) { |
| char orgChar = text.charAt(i); |
| float glyphAdjust = 0; |
| if (!font.hasChar(orgChar)) { |
| if (CharUtilities.isFixedWidthSpace(orgChar)) { |
| //Fixed width space are rendered as spaces so copy/paste works in a reader |
| char ch = font.mapChar(CharUtilities.SPACE); |
| int spaceDiff = font.getCharWidth(ch) - font.getCharWidth(orgChar); |
| glyphAdjust = -(10 * spaceDiff / fontSize); |
| } |
| } |
| |
| if ((wordSpacing != 0) && CharUtilities.isAdjustableSpace(orgChar)) { |
| glyphAdjust += wordSpacing; |
| } |
| current += orgChar; |
| glyphAdjust += letterSpacing; |
| if (dx != null && i < dxl - 1) { |
| glyphAdjust += dx[i + 1]; |
| } |
| |
| if (glyphAdjust != 0) { |
| gen.getOutputStream().write(sb.toString().getBytes(gen.getTextEncoding())); |
| for (int j = 0; j < current.length(); j++) { |
| gen.getOutputStream().write(softFont.getCharCode(current.charAt(j))); |
| } |
| sb = new StringBuffer(); |
| |
| String command = (glyphAdjust > 0) ? "\u001B&a+" : "\u001B&a"; |
| sb.append(command).append(gen.formatDouble2(glyphAdjust / 100.0)).append('H'); |
| |
| current = ""; |
| } |
| } |
| if (!current.equals("")) { |
| gen.getOutputStream().write(sb.toString().getBytes(gen.getTextEncoding())); |
| for (int i = 0; i < current.length(); i++) { |
| gen.getOutputStream().write(softFont.getCharCode(current.charAt(i))); |
| } |
| } |
| } |
| |
| private static final double SAFETY_MARGIN_FACTOR = 0.05; |
| |
| private Rectangle getTextBoundingBox(int x, int y, int letterSpacing, int wordSpacing, |
| int[][] dp, String text, Font font, FontMetricsMapper metrics) { |
| int maxAscent = metrics.getMaxAscent(font.getFontSize()) / 1000; |
| int descent = metrics.getDescender(font.getFontSize()) / 1000; // is negative |
| int safetyMargin = (int) (SAFETY_MARGIN_FACTOR * font.getFontSize()); |
| Rectangle boundingRect = new Rectangle(x, y - maxAscent - safetyMargin, 0, maxAscent |
| - descent + 2 * safetyMargin); |
| |
| int l = text.length(); |
| int[] dx = IFUtil.convertDPToDX(dp); |
| int dxl = (dx != null ? dx.length : 0); |
| |
| if (dx != null && dxl > 0 && dx[0] != 0) { |
| boundingRect.setLocation(boundingRect.x - (int) Math.ceil(dx[0] / 10f), boundingRect.y); |
| } |
| float width = 0.0f; |
| 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]; |
| } |
| |
| width += cw + glyphAdjust; |
| } |
| int extraWidth = font.getFontSize() / 3; |
| boundingRect.setSize((int) Math.ceil(width) + extraWidth, boundingRect.height); |
| return boundingRect; |
| } |
| |
| private void drawTextAsBitmap(final int x, final int y, final int letterSpacing, |
| final int wordSpacing, final int[][] dp, final String text, FontTriplet triplet) |
| throws IFException { |
| // Use Java2D to paint different fonts via bitmap |
| final Font font = getFontInfo().getFontInstance(triplet, state.getFontSize()); |
| |
| // for cursive fonts, so the text isn't clipped |
| FontMetricsMapper mapper; |
| try { |
| mapper = (FontMetricsMapper) getFontInfo().getMetricsFor(font.getFontName()); |
| } catch (Exception t) { |
| throw new RuntimeException(t); |
| } |
| final int maxAscent = mapper.getMaxAscent(font.getFontSize()) / 1000; |
| final int ascent = mapper.getAscender(font.getFontSize()) / 1000; |
| final int descent = mapper.getDescender(font.getFontSize()) / 1000; |
| int safetyMargin = (int) (SAFETY_MARGIN_FACTOR * font.getFontSize()); |
| final int baselineOffset = maxAscent + safetyMargin; |
| |
| final Rectangle boundingBox = getTextBoundingBox(x, y, letterSpacing, wordSpacing, dp, |
| text, font, mapper); |
| final Dimension dim = boundingBox.getSize(); |
| |
| Graphics2DImagePainter painter = new Graphics2DImagePainter() { |
| |
| public void paint(Graphics2D g2d, Rectangle2D area) { |
| if (DEBUG) { |
| g2d.setBackground(Color.LIGHT_GRAY); |
| g2d.clearRect(0, 0, (int) area.getWidth(), (int) area.getHeight()); |
| } |
| g2d.translate(-x, -y + baselineOffset); |
| |
| if (DEBUG) { |
| Rectangle rect = new Rectangle(x, y - maxAscent, 3000, maxAscent); |
| g2d.draw(rect); |
| rect = new Rectangle(x, y - ascent, 2000, ascent); |
| g2d.draw(rect); |
| rect = new Rectangle(x, y, 1000, -descent); |
| g2d.draw(rect); |
| } |
| Java2DPainter painter = new Java2DPainter(g2d, getContext(), getFontInfo(), state); |
| try { |
| painter.drawText(x, y, letterSpacing, wordSpacing, dp, text); |
| } catch (IFException e) { |
| // This should never happen with the Java2DPainter |
| throw new RuntimeException("Unexpected error while painting text", e); |
| } |
| } |
| |
| public Dimension getImageSize() { |
| return dim.getSize(); |
| } |
| |
| }; |
| paintMarksAsBitmap(painter, boundingBox); |
| } |
| |
| /** Saves the current graphics state on the stack. */ |
| private void saveGraphicsState() { |
| graphicContextStack.push(graphicContext); |
| graphicContext = (GraphicContext)graphicContext.clone(); |
| } |
| |
| /** Restores the last graphics state from the stack. */ |
| private void restoreGraphicsState() { |
| graphicContext = graphicContextStack.pop(); |
| } |
| |
| private void concatenateTransformationMatrix(AffineTransform transform) throws IOException { |
| if (!transform.isIdentity()) { |
| graphicContext.transform(transform); |
| changePrintDirection(); |
| } |
| } |
| |
| private Point2D transformedPoint(int x, int y) { |
| return PCLRenderingUtil.transformedPoint(x, y, graphicContext.getTransform(), |
| currentPageDefinition, currentPrintDirection); |
| } |
| |
| private void changePrintDirection() throws IOException { |
| AffineTransform at = graphicContext.getTransform(); |
| int newDir; |
| newDir = PCLRenderingUtil.determinePrintDirection(at); |
| if (newDir != this.currentPrintDirection) { |
| this.currentPrintDirection = newDir; |
| gen.changePrintDirection(this.currentPrintDirection); |
| } |
| } |
| |
| /** |
| * Sets the current cursor position. The coordinates are transformed to the absolute position |
| * on the logical PCL page and then passed on to the PCLGenerator. |
| * @param x the x coordinate (in millipoints) |
| * @param y the y coordinate (in millipoints) |
| */ |
| void setCursorPos(int x, int y) throws IOException { |
| Point2D transPoint = transformedPoint(x, y); |
| gen.setCursorPos(transPoint.getX(), transPoint.getY()); |
| } |
| |
| |
| |
| } |