blob: 86991ecb11fdd39d3e0c32c53bd30905017c8792 [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.intermediate;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.util.DecimalFormatCache;
/**
* Utility functions for the intermediate format.
*/
public final class IFUtil {
private IFUtil() {
}
private static String format(double value) {
if (value == -0.0) {
//Don't allow negative zero because of testing
//See http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.2.3
value = 0.0;
}
return DecimalFormatCache.getDecimalFormat(6).format(value);
}
/**
* Converts an {@link AffineTransform} instance to an SVG style transform method.
* @param transform the transformation matrix
* @param sb the StringBuffer to write the transform method to
* @return the StringBuffer passed to this method
*/
public static StringBuffer toString(AffineTransform transform, StringBuffer sb) {
if (transform.isIdentity()) {
return sb;
}
double[] matrix = new double[6];
transform.getMatrix(matrix);
if (matrix[0] == 1 && matrix[3] == 1 && matrix[1] == 0 && matrix[2] == 0) {
sb.append("translate(");
sb.append(format(matrix[4]));
if (matrix[5] != 0) {
sb.append(',').append(format(matrix[5]));
}
} else {
sb.append("matrix(");
for (int i = 0; i < 6; i++) {
if (i > 0) {
sb.append(',');
}
sb.append(format(matrix[i]));
}
}
sb.append(')');
return sb;
}
/**
* Converts an {@link AffineTransform} array to an SVG style transform method sequence.
* @param transforms the transformation matrix array
* @param sb the StringBuffer to write the transform method sequence to
* @return the StringBuffer passed to this method
*/
public static StringBuffer toString(AffineTransform[] transforms, StringBuffer sb) {
for (int i = 0, c = transforms.length; i < c; i++) {
if (i > 0) {
sb.append(' ');
}
toString(transforms[i], sb);
}
return sb;
}
/**
* Converts an {@link AffineTransform} array to an SVG style transform method sequence.
* @param transforms the transformation matrix array
* @return the formatted array
*/
public static String toString(AffineTransform[] transforms) {
return toString(transforms, new StringBuffer()).toString();
}
/**
* Converts an {@link AffineTransform} instance to an SVG style transform method.
* @param transform the transformation matrix
* @return the formatted array
*/
public static String toString(AffineTransform transform) {
return toString(transform, new StringBuffer()).toString();
}
/**
* Converts an array of integer coordinates into a space-separated string.
* @param coordinates the coordinates
* @return the space-separated array of coordinates
*/
public static String toString(int[] coordinates) {
if (coordinates == null) {
return "";
}
StringBuffer sb = new StringBuffer();
for (int i = 0, c = coordinates.length; i < c; i++) {
if (i > 0) {
sb.append(' ');
}
sb.append(Integer.toString(coordinates[i]));
}
return sb.toString();
}
/**
* Converts a rectangle into a space-separated string.
* @param rect the rectangle
* @return the space-separated array of coordinates
*/
public static String toString(Rectangle rect) {
if (rect == null) {
return "";
}
StringBuffer sb = new StringBuffer();
sb.append(rect.x).append(' ').append(rect.y).append(' ');
sb.append(rect.width).append(' ').append(rect.height);
return sb.toString();
}
/**
* Sets up the fonts on a document handler. If the document handler provides a configurator
* object the configuration from the {@link FopFactory} will be used. Otherwise,
* a default font configuration will be set up.
* @param documentHandler the document handler
* @param fontInfo the font info object (may be null)
* @throws FOPException if an error occurs while setting up the fonts
*/
public static void setupFonts(IFDocumentHandler documentHandler, FontInfo fontInfo)
throws FOPException {
if (fontInfo == null) {
fontInfo = new FontInfo();
}
if (documentHandler instanceof IFSerializer) {
IFSerializer serializer = (IFSerializer)documentHandler;
if (serializer.getMimickedDocumentHandler() != null) {
//Use the mimicked document handler's configurator to set up fonts
documentHandler = serializer.getMimickedDocumentHandler();
}
}
IFDocumentHandlerConfigurator configurator = documentHandler.getConfigurator();
if (configurator != null) {
configurator.setupFontInfo(documentHandler, fontInfo);
} else {
documentHandler.setDefaultFontInfo(fontInfo);
}
}
/**
* Sets up the fonts on a document handler. If the document handler provides a configurator
* object the configuration from the {@link FopFactory} will be used. Otherwise,
* a default font configuration will be set up.
* @param documentHandler the document handler
* @throws FOPException if an error occurs while setting up the fonts
*/
public static void setupFonts(IFDocumentHandler documentHandler) throws FOPException {
setupFonts(documentHandler, null);
}
/**
* Returns the MIME type of the output format that the given document handler is supposed to
* handle. If the document handler is an {@link IFSerializer} it returns the MIME type of the
* document handler it is mimicking.
* @param documentHandler the document handler
* @return the effective MIME type
*/
public static String getEffectiveMIMEType(IFDocumentHandler documentHandler) {
if (documentHandler instanceof IFSerializer) {
IFDocumentHandler mimic = ((IFSerializer)documentHandler).getMimickedDocumentHandler();
if (mimic != null) {
return mimic.getMimeType();
}
}
return documentHandler.getMimeType();
}
/**
* Convert the general gpos 'dp' adjustments to the older 'dx' adjustments.
* This utility method is used to provide backward compatibility in implementations
* of IFPainter that have not yet been upgraded to the general position adjustments format.
* @param dp an array of 4-tuples, expressing [X,Y] placment
* adjustments and [X,Y] advancement adjustments, in that order (may be null)
* @param count if <code>dp</code> is not null, then a count of dp values to convert
* @return if <code>dp</code> is not null, then an array of adjustments to the current
* x position prior to rendering individual glyphs; otherwise, null
*/
public static int[] convertDPToDX ( int[][] dp, int count ) {
int[] dx;
if ( dp != null ) {
dx = new int [ count ];
for ( int i = 0, n = count; i < n; i++ ) {
dx [ i ] = dp [ i ] [ 0 ]; // xPlaAdjust[i]
}
} else {
dx = null;
}
return dx;
}
/**
* Convert the general gpos 'dp' adjustments to the older 'dx' adjustments.
* This utility method is used to provide backward compatibility in implementations
* of IFPainter that have not yet been upgraded to the general position adjustments format.
* @param dp an array of 4-tuples, expressing [X,Y] placment
* adjustments and [X,Y] advancement adjustments, in that order (may be null)
* @return if <code>dp</code> is not null, then an array of adjustments to the current
* x position prior to rendering individual glyphs; otherwise, null
*/
public static int[] convertDPToDX ( int[][] dp ) {
return convertDPToDX ( dp, ( dp != null ) ? dp.length : 0 );
}
/**
* Convert the general gpos 'dp' adjustments to the older 'dx' adjustments.
* This utility method is used to provide backward compatibility in implementations
* of IFPainter that have not yet been upgraded to the general position adjustments format.
* @param dx an array of adjustments to the current x position prior to rendering
* individual glyphs or null
* @param count if <code>dx</code> is not null, then a count of dx values to convert
* @return if <code>dx</code> is not null, then an array of 4-tuples, expressing [X,Y]
* placment adjustments and [X,Y] advancement adjustments, in that order; otherwise, null
*/
public static int[][] convertDXToDP ( int[] dx, int count ) {
int[][] dp;
if ( dx != null ) {
dp = new int [ count ] [ 4 ];
for ( int i = 0, n = count; i < n; i++ ) {
int[] pa = dp [ i ];
int d = dx [ i ];
pa [ 0 ] = d; // xPlaAdjust[i]
pa [ 2 ] = d; // xAdvAdjust[i]
}
} else {
dp = null;
}
return dp;
}
/**
* Convert the general gpos 'dp' adjustments to the older 'dx' adjustments.
* This utility method is used to provide backward compatibility in implementations
* of IFPainter that have not yet been upgraded to the general position adjustments format.
* @param dx an array of adjustments to the current x position prior to rendering
* individual glyphs or null
* @return if <code>dx</code> is not null, then an array of 4-tuples, expressing [X,Y]
* placment adjustments and [X,Y] advancement adjustments, in that order; otherwise, null
*/
public static int[][] convertDXToDP ( int[] dx ) {
return convertDXToDP ( dx, ( dx != null ) ? dx.length : 0 );
}
/**
* Determine if position adjustment is the identity adjustment, i.e., no non-zero adjustment.
* @param pa a 4-tuple, expressing [X,Y] placment and [X,Y] advance adjuustments (may be null)
* @return true if <code>dp</code> is null or contains no non-zero adjustment
*/
public static boolean isPAIdentity ( int[] pa ) {
if ( pa == null ) {
return true;
} else {
for ( int k = 0; k < 4; k++ ) {
if ( pa[k] != 0 ) {
return false;
}
}
return true;
}
}
/**
* Determine if position adjustments is the identity adjustment, i.e., no non-zero adjustment.
* @param dp an array of 4-tuples, expressing [X,Y] placment
* adjustments and [X,Y] advancement adjustments, in that order (may be null)
* @return true if <code>dp</code> is null or contains no non-zero adjustment
*/
public static boolean isDPIdentity ( int[][] dp ) {
if ( dp == null ) {
return true;
} else {
for ( int i = 0, n = dp.length; i < n; i++ ) {
if ( !isPAIdentity ( dp[i] ) ) {
return false;
}
}
return true;
}
}
/**
* Determine if position adjustments comprises only DX adjustments as encoded by
* {@link #convertDPToDX}. Note that if given a set of all all zero position
* adjustments, both this method and {@link #isDPIdentity} will return true;
* however, this method may return true when {@link #isDPIdentity} returns false.
* @param dp an array of 4-tuples, expressing [X,Y] placment
* adjustments and [X,Y] advancement adjustments, in that order (may be null)
* @return true if <code>dp</code> is not null and contains only xPlaAdjust
* and xAdvAdjust values consistent with the output of {@link #convertDPToDX}.
*/
public static boolean isDPOnlyDX ( int[][] dp ) {
if ( dp == null ) {
return false;
} else {
for ( int i = 0, n = dp.length; i < n; i++ ) {
int[] pa = dp[i];
if ( pa[0] != pa[2] ) {
return false;
}
}
return true;
}
}
/**
* Adjust a position adjustments array. If both <code>paDst</code> and <code>paSrc</code> are
* non-null, then <code>paSrc[i]</code> is added to <code>paDst[i]</code>.
* @param paDst a 4-tuple, expressing [X,Y] placment
* and [X,Y] advance adjuustments (may be null)
* @param paSrc a 4-tuple, expressing [X,Y] placment
* and [X,Y] advance adjuustments (may be null)
*/
public static void adjustPA ( int[] paDst, int[] paSrc ) {
if ( ( paDst != null ) && ( paSrc != null ) ) {
assert paDst.length == 4; assert paSrc.length == 4;
for ( int i = 0; i < 4; i++ ) {
paDst[i] += paSrc[i];
}
}
}
/**
* Copy entries from position adjustments.
* @param dp an array of 4-tuples, expressing [X,Y] placment
* adjustments and [X,Y] advancement adjustments, in that order
* @param offset starting offset from which to copy
* @param count number of entries to copy
* @return a deep copy of the count position adjustment entries start at
* offset
*/
public static int[][] copyDP ( int[][] dp, int offset, int count ) {
if ( ( dp == null ) || ( offset > dp.length ) || ( ( offset + count ) > dp.length ) ) {
throw new IllegalArgumentException();
} else {
int[][] dpNew = new int [ count ] [ 4 ];
for ( int i = 0, n = count; i < n; i++ ) {
int[] paDst = dpNew [ i ];
int[] paSrc = dp [ i + offset ];
for ( int k = 0; k < 4; k++ ) {
paDst [ k ] = paSrc [ k ];
}
}
return dpNew;
}
}
}