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

org.apache.fop.fo.PropertyList 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: PropertyList.java 1805173 2017-08-16 10:50:04Z ssteiner $ */

package org.apache.fop.fo;

// Java
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.xml.sax.Attributes;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.xmlgraphics.util.QName;

import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fo.expr.PropertyException;
import org.apache.fop.fo.properties.CommonAbsolutePosition;
import org.apache.fop.fo.properties.CommonAural;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.CommonFont;
import org.apache.fop.fo.properties.CommonHyphenation;
import org.apache.fop.fo.properties.CommonMarginBlock;
import org.apache.fop.fo.properties.CommonMarginInline;
import org.apache.fop.fo.properties.CommonRelativePosition;
import org.apache.fop.fo.properties.CommonTextDecoration;
import org.apache.fop.fo.properties.Property;
import org.apache.fop.fo.properties.PropertyMaker;

/**
 * Class containing the collection of properties for a given FObj.
 */
public abstract class PropertyList {

    private static boolean[] inheritableProperty;

    static {
        inheritableProperty = new boolean[Constants.PROPERTY_COUNT + 1];
        PropertyMaker maker = null;
        for (int prop = 1; prop <= Constants.PROPERTY_COUNT; prop++) {
            maker = findMaker(prop);
            inheritableProperty[prop] = (maker != null && maker.isInherited());
        }
    }

    /** reference to the parent FO's propertyList **/
    protected PropertyList parentPropertyList;
    private FObj fobj;

    private static Log log = LogFactory.getLog(PropertyList.class);

    private final UnknownPropertyHandler unknownPropertyHandler = new UnknownPropertyHandler();

    /**
     * Basic constructor.
     * @param fObjToAttach  the FO this PropertyList should be attached to
     * @param parentPropertyList the PropertyList belonging to the new objects
     * parent
     */
    public PropertyList(FObj fObjToAttach, PropertyList parentPropertyList) {
        this.fobj = fObjToAttach;
        this.parentPropertyList = parentPropertyList;
    }

    /**
     * @return the FObj object to which this propertyList is attached
     */
    public FObj getFObj() {
        return this.fobj;
    }

    /**
     * @return the FObj object attached to the parentPropertyList
     */
    public FObj getParentFObj() {
        if (parentPropertyList != null) {
            return parentPropertyList.getFObj();
        } else {
            return null;
        }
    }

    /**
     * Adds an unknown property value to the property list so that if
     * necessary, a warning can be displayed.
     * @param propertyValue The unknown property value
     * @param output The output of the property to validate
     * @param property The original property containing the full value
     */
    public void validatePropertyValue(String propertyValue, Property output, Property property) {
        unknownPropertyHandler.validatePropertyValue(propertyValue, output, property);
    }

    /**
     * Gets the current list of unknown property values
     * @return The set containing the list of unknown property values
     */
    public Map getUnknownPropertyValues() {
        return unknownPropertyHandler.getUnknownPropertyValues();
    }

    /**
     * @return the FObj object attached to the parentPropetyList
     */
    public PropertyList getParentPropertyList() {
        return parentPropertyList;
    }

    /**
     * Return the value explicitly specified on this FO.
     * @param propId The id of the property whose value is desired.
     * @return The value if the property is explicitly set or set by
     * a shorthand property, otherwise null.
     * @throws PropertyException ...
     */
    public Property getExplicitOrShorthand(int propId) throws PropertyException {
        /* Handle request for one part of a compound property */
        Property p = getExplicit(propId);
        if (p == null) {
            p = getShorthand(propId);
        }
        return p;
    }

    /**
     * A class to handle unknown shorthand property values e.g. border="solit 1pt"
     */
    private static class UnknownPropertyHandler {

        /**
         * A list of unknown properties identified by the value and property in which its featured
         */
        private Map unknownPropertyValues = new HashMap();

        /**
         * A list of known properties which have already been processed
         */
        private Set knownProperties = new HashSet();

        void validatePropertyValue(String propertyValue, Property output, Property property) {
            if (!knownProperties.contains(property) && output == null) {
                if (propertyValue != null) {
                    unknownPropertyValues.put(propertyValue, property);
                }
            } else {
                knownProperties.add(property);
            }
        }

        Map getUnknownPropertyValues() {
            return unknownPropertyValues;
        }
    }

    /**
     * Return the value explicitly specified on this FO.
     * @param propId The ID of the property whose value is desired.
     * @return The value if the property is explicitly set, otherwise null.
     */
    public abstract Property getExplicit(int propId);

    /**
     * Set an value defined explicitly on this FO.
     * @param propId The ID of the property to set.
     * @param value The value of the property.
     */
    public abstract void putExplicit(int propId, Property value);

    /**
     * Return the value of this property inherited by this FO.
     * Implements the inherited-property-value function.
     * The property must be inheritable!
     * @param propId The ID of the property whose value is desired.
     * @return The inherited value, otherwise null.
     * @throws PropertyException ...
     */
    public Property getInherited(int propId) throws PropertyException {

        if (isInherited(propId)) {
            return getFromParent(propId);
        } else {
            // return the "initial" value
            return makeProperty(propId);
        }
    }

    /**
     * Return the property on the current FlowObject. If it isn't set explicitly,
     * this will try to compute it based on other properties, or if it is
     * inheritable, to return the inherited value. If all else fails, it returns
     * the default value.
     * @param propId The Constants ID of the property whose value is desired.
     * @return the Property corresponding to that name
     * @throws PropertyException if there is a problem evaluating the property
     */
    public Property get(int propId) throws PropertyException {
        return get(propId, true, true);
    }

    /**
     * Return the property on the current FlowObject. Depending on the passed flags,
     * this will try to compute it based on other properties, or if it is
     * inheritable, to return the inherited value. If all else fails, it returns
     * the default value.
     * @param propId    the property's id
     * @param bTryInherit   true for inherited properties, or when the inherited
     *                      value is needed
     * @param bTryDefault   true when the default value may be used as a last resort
     * @return the property
     * @throws PropertyException if there is a problem evaluating the property
     */
    public Property get(int propId, boolean bTryInherit,
                         boolean bTryDefault) throws PropertyException {

        PropertyMaker propertyMaker = findMaker(propId & Constants.PROPERTY_MASK);
        if (propertyMaker != null) {
            return propertyMaker.get(propId & Constants.COMPOUND_MASK, this,
                                         bTryInherit, bTryDefault);
        }
        return null;
    }

    /**
     * Return the "nearest" specified value for the given property.
     * Implements the from-nearest-specified-value function.
     * @param propId The ID of the property whose value is desired.
     * @return The computed value if the property is explicitly set on some
     * ancestor of the current FO, else the initial value.
     * @throws PropertyException if there an error occurred when getting the property
     */
    public Property getNearestSpecified(int propId) throws PropertyException {
        Property p = null;
        PropertyList pList = parentPropertyList;

        while (pList != null) {
            p = pList.getExplicit(propId);
            if (p != null) {
                return p;
            } else {
                pList = pList.parentPropertyList;
            }
        }

        // If no explicit value found on any of the ancestor-nodes,
        // return initial (default) value.
        return makeProperty(propId);
    }

    /**
     * Return the value of this property on the parent of this FO.
     * Implements the from-parent function.
     * @param propId The Constants ID of the property whose value is desired.
     * @return The computed value on the parent or the initial value if this
     * FO is the root or is in a different namespace from its parent.
     * @throws PropertyException ...
     */
    public Property getFromParent(int propId) throws PropertyException {
        if (parentPropertyList != null) {
            return parentPropertyList.get(propId);
        } else {
            return makeProperty(propId);
        }
    }

    /**
     * Select a writing mode dependent property ID based on value of writing mode property.
     * @param lrtb the property ID to return under lrtb writingmode.
     * @param rltb the property ID to return under rltb writingmode.
     * @param tbrl the property ID to return under tbrl writingmode.
     * @param tblr the property ID to return under tblr writingmode.
     * @return one of the property IDs, depending on the writing mode.
     */
    public int selectFromWritingMode(int lrtb, int rltb, int tbrl, int tblr) {
        int propID;
        try {
            switch (get(Constants.PR_WRITING_MODE).getEnum()) {
            case Constants.EN_LR_TB:
                propID = lrtb;
                break;
            case Constants.EN_RL_TB:
                propID = rltb;
                break;
            case Constants.EN_TB_RL:
                propID = tbrl;
                break;
            case Constants.EN_TB_LR:
                propID = tblr;
                break;
            default:
            propID = -1;
                break;
            }
        } catch (PropertyException e) {
            propID = -1;
        }
        return propID;
    }

    private String addAttributeToList(Attributes attributes,
                                    String attributeName) throws ValidationException {
        String attributeValue = attributes.getValue(attributeName);
        if (attributeValue != null) {
            convertAttributeToProperty(attributes, attributeName, attributeValue);
        }
        return attributeValue;
    }

    /**
     * 

Adds the attributes, passed in by the parser to the PropertyList.

*

Note that certain attributes are given priority in terms of order of * processing due to conversion dependencies, where the order is as follows:

*
    *
  1. writing-mode
  2. *
  3. column-number
  4. *
  5. number-columns-spanned
  6. *
  7. font
  8. *
  9. font-size
  10. *
  11. all others in order of appearance
  12. *
* * @param attributes Collection of attributes passed to us from the parser. * @throws ValidationException if there is an attribute that does not * map to a property id (strict validation only) */ public void addAttributesToList(Attributes attributes) throws ValidationException { /* * Give writing-mode highest conversion priority. */ addAttributeToList(attributes, "writing-mode"); /* * If column-number/number-columns-spanned are specified, then we * need them before all others (possible from-table-column() on any * other property further in the list... */ addAttributeToList(attributes, "column-number"); addAttributeToList(attributes, "number-columns-spanned"); /* * If font-size is set on this FO, must set it first, since * other attributes specified in terms of "ems" depend on it. */ String checkValue = addAttributeToList(attributes, "font"); if (checkValue == null || "".equals(checkValue)) { /* * font shorthand wasn't specified, so still need to process * explicit font-size */ addAttributeToList(attributes, "font-size"); } String attributeNS; String attributeName; String attributeValue; FOUserAgent userAgent = getFObj().getUserAgent(); for (int i = 0; i < attributes.getLength(); i++) { /* convert all attributes with the same namespace as the fo element * the "xml:lang" and "xml:base" properties are special cases */ attributeNS = attributes.getURI(i); attributeName = attributes.getQName(i); attributeValue = attributes.getValue(i); if (attributeNS == null || attributeNS.length() == 0 || "xml:lang".equals(attributeName) || "xml:base".equals(attributeName)) { convertAttributeToProperty(attributes, attributeName, attributeValue); } else if (!userAgent.isNamespaceIgnored(attributeNS)) { ElementMapping mapping = userAgent.getElementMappingRegistry().getElementMapping( attributeNS); QName attr = new QName(attributeNS, attributeName); if (mapping != null) { if (mapping.isAttributeProperty(attr) && mapping.getStandardPrefix() != null) { convertAttributeToProperty(attributes, mapping.getStandardPrefix() + ":" + attr.getLocalName(), attributeValue); } else { getFObj().addForeignAttribute(attr, attributeValue); } } else { handleInvalidProperty(attr); } } } } /** * Validates a property name. * @param propertyName the property name to check * @return true if the base property name and the subproperty name (if any) * can be correctly mapped to an id */ protected boolean isValidPropertyName(String propertyName) { int propId = FOPropertyMapping.getPropertyId( findBasePropertyName(propertyName)); int subpropId = FOPropertyMapping.getSubPropertyId( findSubPropertyName(propertyName)); return !(propId == -1 || (subpropId == -1 && findSubPropertyName(propertyName) != null)); } public Property getPropertyForAttribute(Attributes attributes, String attributeName, String attributeValue) throws FOPException { if (attributeValue != null) { if (attributeName.startsWith("xmlns:") || "xmlns".equals(attributeName)) { return null; } String basePropertyName = findBasePropertyName(attributeName); String subPropertyName = findSubPropertyName(attributeName); int propId = FOPropertyMapping.getPropertyId(basePropertyName); int subpropId = FOPropertyMapping.getSubPropertyId(subPropertyName); if (propId == -1 || (subpropId == -1 && subPropertyName != null)) { return null; } return getExplicit(propId); } return null; } /** * * @param attributes Collection of attributes * @param attributeName Attribute name to convert * @param attributeValue Attribute value to assign to property * @throws ValidationException in case the property name is invalid * for the FO namespace */ private void convertAttributeToProperty(Attributes attributes, String attributeName, String attributeValue) throws ValidationException { if (attributeName.startsWith("xmlns:") || "xmlns".equals(attributeName)) { /* Ignore namespace declarations if the XML parser/XSLT processor * reports them as 'regular' attributes */ return; } if (attributeValue != null) { /* Handle "compound" properties, ex. space-before.minimum */ String basePropertyName = findBasePropertyName(attributeName); String subPropertyName = findSubPropertyName(attributeName); int propId = FOPropertyMapping.getPropertyId(basePropertyName); int subpropId = FOPropertyMapping.getSubPropertyId(subPropertyName); if (propId == -1 || (subpropId == -1 && subPropertyName != null)) { handleInvalidProperty(new QName(null, attributeName)); } FObj parentFO = fobj.findNearestAncestorFObj(); PropertyMaker propertyMaker = findMaker(propId); if (propertyMaker == null) { log.warn("No PropertyMaker registered for " + attributeName + ". Ignoring property."); return; } try { Property prop = null; if (subPropertyName == null) { // base attribute only found /* Do nothing if the base property has already been created. * This is e.g. the case when a compound attribute was * specified before the base attribute; in these cases * the base attribute was already created in * findBaseProperty() */ if (getExplicit(propId) != null) { return; } prop = propertyMaker.make(this, attributeValue, parentFO); } else { // e.g. "leader-length.maximum" Property baseProperty = findBaseProperty(attributes, parentFO, propId, basePropertyName, propertyMaker); prop = propertyMaker.make(baseProperty, subpropId, this, attributeValue, parentFO); } if (prop != null) { putExplicit(propId, prop); } } catch (PropertyException e) { fobj.getFOValidationEventProducer().invalidPropertyValue(this, fobj.getName(), attributeName, attributeValue, e, fobj.locator); } } } private Property findBaseProperty(Attributes attributes, FObj parentFO, int propId, String basePropertyName, PropertyMaker propertyMaker) throws PropertyException { /* If the baseProperty has already been created, return it * e.g. */ Property baseProperty = getExplicit(propId); if (baseProperty != null) { return baseProperty; } /* Otherwise If it is specified later in this list of Attributes, create it now * e.g. */ String basePropertyValue = attributes.getValue(basePropertyName); if (basePropertyValue != null && propertyMaker != null) { baseProperty = propertyMaker.make(this, basePropertyValue, parentFO); return baseProperty; } return null; // could not find base property } /** * Handles an invalid property. * @param attr the invalid attribute * @throws ValidationException if an exception needs to be thrown depending on the * validation settings */ protected void handleInvalidProperty(QName attr) throws ValidationException { if (!attr.getQName().startsWith("xmlns")) { fobj.getFOValidationEventProducer().invalidProperty(this, fobj.getName(), attr, true, fobj.locator); } } /** * Finds the first or base part (up to any period) of an attribute name. * For example, if input is "space-before.minimum", should return * "space-before". * @param attributeName String to be atomized * @return the base portion of the attribute */ protected static String findBasePropertyName(String attributeName) { int separatorCharIndex = attributeName.indexOf('.'); String basePropertyName = attributeName; if (separatorCharIndex > -1) { basePropertyName = attributeName.substring(0, separatorCharIndex); } return basePropertyName; } /** * Finds the second or sub part (portion past any period) of an attribute * name. For example, if input is "space-before.minimum", should return * "minimum". * @param attributeName String to be atomized * @return the sub portion of the attribute */ protected static String findSubPropertyName(String attributeName) { int separatorCharIndex = attributeName.indexOf('.'); String subpropertyName = null; if (separatorCharIndex > -1) { subpropertyName = attributeName.substring(separatorCharIndex + 1); } return subpropertyName; } /** * @param propId ID of property * @return new Property object * @throws PropertyException if there's a problem while processing the property */ private Property getShorthand(int propId) throws PropertyException { PropertyMaker propertyMaker = findMaker(propId); if (propertyMaker != null) { return propertyMaker.getShorthand(this); } else { //log.error("no Maker for " + propertyName); return null; } } /** * @param propId ID of property * @return new Property object * @throws PropertyException if there's a problem while processing the property */ private Property makeProperty(int propId) throws PropertyException { PropertyMaker propertyMaker = findMaker(propId); if (propertyMaker != null) { return propertyMaker.make(this); } else { //log.error("property " + propertyName // + " ignored"); } return null; } /** * @param propId ID of property * @return isInherited value from the requested Property.Maker */ private boolean isInherited(int propId) { return inheritableProperty[propId]; } /** * @param propId Id of property * @return the Property.Maker for this property */ private static PropertyMaker findMaker(int propId) { if (propId < 1 || propId > Constants.PROPERTY_COUNT) { return null; } else { return FObj.getPropertyMakerFor(propId); } } /** * Constructs a BorderAndPadding object. * @return a BorderAndPadding object * @throws PropertyException if there's a problem while processing the properties */ public CommonBorderPaddingBackground getBorderPaddingBackgroundProps() throws PropertyException { return CommonBorderPaddingBackground.getInstance(this); } /** * Constructs a CommonHyphenation object. * @return the CommonHyphenation object * @throws PropertyException if there's a problem while processing the properties */ public CommonHyphenation getHyphenationProps() throws PropertyException { return CommonHyphenation.getInstance(this); } /** * Constructs a CommonMarginBlock object. * @return the CommonMarginBlock object * @throws PropertyException if there's a problem while processing the properties */ public CommonMarginBlock getMarginBlockProps() throws PropertyException { return new CommonMarginBlock(this); } /** * Constructs a CommonMarginInline object. * @return the CommonMarginInline object * @throws PropertyException if there's a problem while processing the properties */ public CommonMarginInline getMarginInlineProps() throws PropertyException { return new CommonMarginInline(this); } /** * Constructs a CommonAural object. * @return the CommonAural object * @throws PropertyException if there's a problem while processing the properties */ public CommonAural getAuralProps() throws PropertyException { CommonAural props = new CommonAural(this); return props; } /** * Constructs a RelativePositionProps objects. * @return a RelativePositionProps object * @throws PropertyException if there's a problem while processing the properties */ public CommonRelativePosition getRelativePositionProps() throws PropertyException { return new CommonRelativePosition(this); } /** * Constructs a CommonAbsolutePosition object. * @return the CommonAbsolutePosition object * @throws PropertyException if there's a problem while processing the properties */ public CommonAbsolutePosition getAbsolutePositionProps() throws PropertyException { return new CommonAbsolutePosition(this); } /** * Constructs a CommonFont object. * * @return A CommonFont object * @throws PropertyException if there's a problem while processing the properties */ public CommonFont getFontProps() throws PropertyException { return CommonFont.getInstance(this); } /** * Constructs a CommonTextDecoration object. * @return a CommonTextDecoration object * @throws PropertyException if there's a problem while processing the properties */ public CommonTextDecoration getTextDecorationProps() throws PropertyException { return CommonTextDecoration.createFromPropertyList(this); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy