All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.fop.render.txt.TXTRenderer Maven / Gradle / Ivy

Go to download

Apache FOP (Formatting Objects Processor) is the world's first print formatter driven by XSL formatting objects (XSL-FO) and the world's first output independent formatter. It is a Java application that reads a formatting object (FO) tree and renders the resulting pages to a specified output. Output formats currently supported include PDF, PCL, PS, AFP, TIFF, PNG, SVG, XML (area tree representation), Print, AWT and TXT. The primary output target is PDF.

There is a newer version: 2.9
Show newest version
/*
 * 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: TXTRenderer.java 830293 2009-10-27 19:07:52Z vhennebert $ */

package org.apache.fop.render.txt;

import java.awt.Color;
import java.awt.Point;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;

import org.apache.xmlgraphics.util.UnitConv;

import org.apache.fop.apps.FOPException;
import org.apache.fop.area.Area;
import org.apache.fop.area.CTM;
import org.apache.fop.area.PageViewport;
import org.apache.fop.area.inline.Image;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.render.AbstractPathOrientedRenderer;
import org.apache.fop.render.txt.border.AbstractBorderElement;
import org.apache.fop.render.txt.border.BorderManager;

/**
 * Renderer that renders areas to plain text.
 *
 * @author Art Welch
 * @author Mark Lillywhite (to use
 *         the new Renderer interface)
 */
public class TXTRenderer extends AbstractPathOrientedRenderer {

    private static final char LIGHT_SHADE = '\u2591';

    private static final char MEDIUM_SHADE = '\u2592';

    private static final char DARK_SHADE = '\u2593';

    private static final char FULL_BLOCK = '\u2588';

    private static final char IMAGE_CHAR = '#';

    /**The stream for output */
    private OutputStream outputStream;

    /** The current stream to add Text commands to. */
    private TXTStream currentStream;

    /** Buffer for text. */
    private StringBuffer[] charData;

    /** Buffer for background and images. */
    private StringBuffer[] decoData;

    /** Height of one symbol in Courier font size of 10pt. */
    public static final int CHAR_HEIGHT = 7860;

    /** Width of one symbol in Courier font size of 10pt. */
    public static final int CHAR_WIDTH = 6000;

    /** Current processing page width. */
    private int pageWidth;

    /** Current processing page height. */
    private int pageHeight;

    /**
     * Every line except the last line on a page (which will end with
     * pageEnding) will be terminated with this string.
     */
    private final String lineEnding = "\r\n";

    /** Every page except the last one will end with this string. */
    private final String pageEnding = "\f";

    /** Equals true, if current page is first. */
    private boolean firstPage = false;

    /** Manager for storing border's information. */
    private BorderManager bm;

    /** Char for current filling. */
    private char fillChar;

    /** Saves current coordinate transformation. */
    private final TXTState currentState = new TXTState();

    private String encoding;

    /**
     * Constructs a newly allocated TXTRenderer object.
     */
    public TXTRenderer() {
    }

    /** {@inheritDoc} */
    public String getMimeType() {
        return "text/plain";
    }

    /**
     * Sets the encoding of the target file.
     * @param encoding the encoding, null to select the default encoding (UTF-8)
     */
    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

    /**
     * Indicates if point (x, y) lay inside currentPage.
     *
     * @param x x coordinate
     * @param y y coordinate
     * @return true if point lay inside page
     */
    public boolean isLayInside(int x, int y) {
        return (x >= 0) && (x < pageWidth) && (y >= 0) && (y < pageHeight);
    }

    /**
     * Add char to text buffer.
     *
     * @param x  x coordinate
     * @param y  y coordinate
     * @param ch  char to add
     * @param ischar boolean, repersenting is character adding to text buffer
     */
    protected void addChar(int x, int y, char ch, boolean ischar) {
        Point point = currentState.transformPoint(x, y);
        putChar(point.x, point.y, ch, ischar);
    }

    /**
     * Add char to text or background buffer.
     *
     * @param x x coordinate
     * @param y x coordinate
     * @param ch char to add
     * @param ischar indicates if it char or background
     */
    protected void putChar(int x, int y, char ch, boolean ischar) {
        if (isLayInside(x, y)) {
            StringBuffer sb = ischar ? charData[y] : decoData[y];
            while (sb.length() <= x) {
                sb.append(' ');
            }
            sb.setCharAt(x, ch);
        }
    }

    /**
     * Adds string to text buffer (charData). 

* Chars of string map in turn. * * @param row x coordinate * @param col y coordinate * @param s string to add */ protected void addString(int row, int col, String s) { for (int l = 0; l < s.length(); l++) { addChar(col + l, row, s.charAt(l), true); } } /** * Render TextArea to Text. * * @param area inline area to render */ protected void renderText(TextArea area) { int col = Helper.ceilPosition(this.currentIPPosition, CHAR_WIDTH); int row = Helper.ceilPosition(this.currentBPPosition, CHAR_HEIGHT); String s = area.getText(); addString(row, col, s); super.renderText(area); } /** * {@inheritDoc} */ public void renderPage(PageViewport page) throws IOException, FOPException { if (firstPage) { firstPage = false; } else { currentStream.add(pageEnding); } Rectangle2D bounds = page.getViewArea(); double width = bounds.getWidth(); double height = bounds.getHeight(); pageWidth = Helper.ceilPosition((int) width, CHAR_WIDTH); pageHeight = Helper.ceilPosition((int) height, CHAR_HEIGHT); // init buffers charData = new StringBuffer[pageHeight]; decoData = new StringBuffer[pageHeight]; for (int i = 0; i < pageHeight; i++) { charData[i] = new StringBuffer(); decoData[i] = new StringBuffer(); } bm = new BorderManager(pageWidth, pageHeight, currentState); super.renderPage(page); flushBorderToBuffer(); flushBuffer(); } /** * Projects current page borders (i.e.bm) to buffer for * background and images (i.e.decoData). */ private void flushBorderToBuffer() { for (int x = 0; x < pageWidth; x++) { for (int y = 0; y < pageHeight; y++) { Character c = bm.getCharacter(x, y); if (c != null) { putChar(x, y, c.charValue(), false); } } } } /** * Write out the buffer to output stream. */ private void flushBuffer() { for (int row = 0; row < pageHeight; row++) { StringBuffer cr = charData[row]; StringBuffer dr = decoData[row]; StringBuffer outr = null; if (cr != null && dr == null) { outr = cr; } else if (dr != null && cr == null) { outr = dr; } else if (cr != null && dr != null) { int len = dr.length(); if (cr.length() > len) { len = cr.length(); } outr = new StringBuffer(); for (int countr = 0; countr < len; countr++) { if (countr < cr.length() && cr.charAt(countr) != ' ') { outr.append(cr.charAt(countr)); } else if (countr < dr.length()) { outr.append(dr.charAt(countr)); } else { outr.append(' '); } } } if (outr != null) { currentStream.add(outr.toString()); } if (row < pageHeight) { currentStream.add(lineEnding); } } } /** * {@inheritDoc} */ public void startRenderer(OutputStream os) throws IOException { log.info("Rendering areas to TEXT."); this.outputStream = os; currentStream = new TXTStream(os); currentStream.setEncoding(this.encoding); firstPage = true; } /** * {@inheritDoc} */ public void stopRenderer() throws IOException { log.info("writing out TEXT"); outputStream.flush(); super.stopRenderer(); } /** * Does nothing. * {@inheritDoc} */ protected void restoreStateStackAfterBreakOut(List breakOutList) { } /** * Does nothing. * @return null * {@inheritDoc} */ protected List breakOutOfStateStack() { return null; } /** * Does nothing. * {@inheritDoc} */ protected void saveGraphicsState() { currentState.push(new CTM()); } /** * Does nothing. * {@inheritDoc} */ protected void restoreGraphicsState() { currentState.pop(); } /** * Does nothing. * {@inheritDoc} */ protected void beginTextObject() { } /** * Does nothing. * {@inheritDoc} */ protected void endTextObject() { } /** * Does nothing. * {@inheritDoc} */ protected void clip() { } /** * Does nothing. * {@inheritDoc} */ protected void clipRect(float x, float y, float width, float height) { } /** * Does nothing. * {@inheritDoc} */ protected void moveTo(float x, float y) { } /** * Does nothing. * {@inheritDoc} */ protected void lineTo(float x, float y) { } /** * Does nothing. * {@inheritDoc} */ protected void closePath() { } /** * Fills rectangle startX, startY, width, height with char * charToFill. * * @param startX x-coordinate of upper left point * @param startY y-coordinate of upper left point * @param width width of rectangle * @param height height of rectangle * @param charToFill filling char */ private void fillRect(int startX, int startY, int width, int height, char charToFill) { for (int x = startX; x < startX + width; x++) { for (int y = startY; y < startY + height; y++) { addChar(x, y, charToFill, false); } } } /** * Fills a rectangular area with the current filling char. * {@inheritDoc} */ protected void fillRect(float x, float y, float width, float height) { fillRect(bm.getStartX(), bm.getStartY(), bm.getWidth(), bm.getHeight(), fillChar); } /** * Changes current filling char. * {@inheritDoc} */ protected void updateColor(Color col, boolean fill) { if (col == null) { return; } // fillShade evaluation was taken from fop-0.20.5 // TODO: This fillShase is catually the luminance component of the color // transformed to the YUV (YPrBb) Colorspace. It should use standard // Java methods for its conversion instead of the formula given here. double fillShade = 0.30f / 255f * col.getRed() + 0.59f / 255f * col.getGreen() + 0.11f / 255f * col.getBlue(); fillShade = 1 - fillShade; if (fillShade > 0.8f) { fillChar = FULL_BLOCK; } else if (fillShade > 0.6f) { fillChar = DARK_SHADE; } else if (fillShade > 0.4f) { fillChar = MEDIUM_SHADE; } else if (fillShade > 0.2f) { fillChar = LIGHT_SHADE; } else { fillChar = ' '; } } /** {@inheritDoc} */ protected void drawImage(String url, Rectangle2D pos, Map foreignAttributes) { //No images are painted here } /** * Fills image rectangle with a IMAGE_CHAR. * * @param image the base image * @param pos the position of the image */ public void renderImage(Image image, Rectangle2D pos) { int x1 = Helper.ceilPosition(currentIPPosition, CHAR_WIDTH); int y1 = Helper.ceilPosition(currentBPPosition, CHAR_HEIGHT); int width = Helper.ceilPosition((int) pos.getWidth(), CHAR_WIDTH); int height = Helper.ceilPosition((int) pos.getHeight(), CHAR_HEIGHT); fillRect(x1, y1, width, height, IMAGE_CHAR); } /** * Returns the closest integer to the multiplication of a number and 1000. * * @param x the value of the argument, multiplied by * 1000 and rounded * @return the value of the argument multiplied by * 1000 and rounded to the nearest integer */ protected int toMilli(float x) { return Math.round(x * 1000f); } /** * Adds one element of border. * * @param x x coordinate * @param y y coordinate * @param style integer, representing border style * @param type integer, representing border element type */ private void addBitOfBorder(int x, int y, int style, int type) { Point point = currentState.transformPoint(x, y); if (isLayInside(point.x, point.y)) { bm.addBorderElement(point.x, point.y, style, type); } } /** * {@inheritDoc} */ protected void drawBorderLine(float x1, float y1, float x2, float y2, boolean horz, boolean startOrBefore, int style, Color col) { int borderHeight = bm.getHeight(); int borderWidth = bm.getWidth(); int borderStartX = bm.getStartX(); int borderStartY = bm.getStartY(); int x, y; if (horz && startOrBefore) { // BEFORE x = borderStartX; y = borderStartY; } else if (horz && !startOrBefore) { // AFTER x = borderStartX; y = borderStartY + borderHeight - 1; } else if (!horz && startOrBefore) { // START x = borderStartX; y = borderStartY; } else { // END x = borderStartX + borderWidth - 1; y = borderStartY; } int dx, dy, length, startType, endType; if (horz) { length = borderWidth; dx = 1; dy = 0; startType = 1 << AbstractBorderElement.RIGHT; endType = 1 << AbstractBorderElement.LEFT; } else { length = borderHeight; dx = 0; dy = 1; startType = 1 << AbstractBorderElement.DOWN; endType = 1 << AbstractBorderElement.UP; } addBitOfBorder(x, y, style, startType); for (int i = 0; i < length - 2; i++) { x += dx; y += dy; addBitOfBorder(x, y, style, startType + endType); } x += dx; y += dy; addBitOfBorder(x, y, style, endType); } /** * {@inheritDoc} */ protected void drawBackAndBorders(Area area, float startx, float starty, float width, float height) { bm.setWidth(Helper.ceilPosition(toMilli(width), CHAR_WIDTH)); bm.setHeight(Helper.ceilPosition(toMilli(height), CHAR_HEIGHT)); bm.setStartX(Helper.ceilPosition(toMilli(startx), CHAR_WIDTH)); bm.setStartY(Helper.ceilPosition(toMilli(starty), CHAR_HEIGHT)); super.drawBackAndBorders(area, startx, starty, width, height); } /** * {@inheritDoc} */ protected void startVParea(CTM ctm, Rectangle2D clippingRect) { currentState.push(ctm); } /** * {@inheritDoc} */ protected void endVParea() { currentState.pop(); } /** {@inheritDoc} */ protected void concatenateTransformationMatrix(AffineTransform at) { currentState.push(new CTM(UnitConv.ptToMpt(at))); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy