/* * Copyright 2000-2014 Vaadin Ltd. * * Licensed 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. */ package com.vaadin.data; import java.io.Serializable; import java.util.Collection; import java.util.List; import com.vaadin.data.util.filter.SimpleStringFilter; import com.vaadin.data.util.filter.UnsupportedFilterException; /** *

* A specialized set of identified Items. Basically the Container is a set of * {@link Item}s, but it imposes certain constraints on its contents. These * constraints state the following: *

* * * *

* The Container can be visualized as a representation of a relational database * table. Each Item in the Container represents a row in the table, and all * cells in a column (identified by a Property ID) have the same data type. Note * that as with the cells in a database table, no Property in a Container may be * empty, though they may contain null values. *

* *

* Note that though uniquely identified, the Items in a Container are not * necessarily {@link Container.Ordered ordered} or {@link Container.Indexed * indexed}. *

* *

* Containers can derive Item ID's from the item properties or use other, * container specific or user specified identifiers. *

* *

* If a container is {@link Filterable filtered} or {@link Sortable sorted}, * most of the the methods of the container interface and its subinterfaces * (container size, {@link #containsId(Object)}, iteration and indices etc.) * relate to the filtered and sorted view, not to the full container contents. * See individual method javadoc for exceptions to this (adding and removing * items). *

* *

* *

* *

* The Container interface is split to several subinterfaces so that a class can * implement only the ones it needs. *

* * @author Vaadin Ltd * @since 3.0 */ public interface Container extends Serializable { /** * Gets the {@link Item} with the given Item ID from the Container. If the * Container does not contain the requested Item, null is * returned. *

* Containers should not return Items that are filtered out. * * @param itemId * ID of the {@link Item} to retrieve * @return the {@link Item} with the given ID or null if the * Item is not found in the Container */ public Item getItem(Object itemId); /** * Gets the ID's of all Properties stored in the Container. The ID's cannot * be modified through the returned collection. * * @return unmodifiable collection of Property IDs */ public Collection getContainerPropertyIds(); /** * Gets the ID's of all visible (after filtering and sorting) Items stored * in the Container. The ID's cannot be modified through the returned * collection. *

* If the container is {@link Ordered}, the collection returned by this * method should follow that order. If the container is {@link Sortable}, * the items should be in the sorted order. *

* Calling this method for large lazy containers can be an expensive * operation and should be avoided when practical. * * @return unmodifiable collection of Item IDs */ public Collection getItemIds(); /** * Gets the Property identified by the given itemId and propertyId from the * Container. If the Container does not contain the item or it is filtered * out, or the Container does not have the Property, null is * returned. * * @param itemId * ID of the visible Item which contains the Property * @param propertyId * ID of the Property to retrieve * @return Property with the given ID or null */ public Property getContainerProperty(Object itemId, Object propertyId); /** * Gets the data type of all Properties identified by the given Property ID. * * @param propertyId * ID identifying the Properties * @return data type of the Properties */ public Class getType(Object propertyId); /** * Gets the number of visible Items in the Container. *

* Filtering can hide items so that they will not be visible through the * container API. * * @return number of Items in the Container */ public int size(); /** * Tests if the Container contains the specified Item. *

* Filtering can hide items so that they will not be visible through the * container API, and this method should respect visibility of items (i.e. * only indicate visible items as being in the container) if feasible for * the container. * * @param itemId * ID the of Item to be tested * @return boolean indicating if the Container holds the specified Item */ public boolean containsId(Object itemId); /** * Creates a new Item with the given ID in the Container. * *

* The new Item is returned, and it is ready to have its Properties * modified. Returns null if the operation fails or the * Container already contains a Item with the given ID. *

* *

* This functionality is optional. *

* * @param itemId * ID of the Item to be created * @return Created new Item, or null in case of a failure * @throws UnsupportedOperationException * if adding an item with an explicit item ID is not supported * by the container */ public Item addItem(Object itemId) throws UnsupportedOperationException; /** * Creates a new Item into the Container, and assign it an automatic ID. * *

* The new ID is returned, or null if the operation fails. * After a successful call you can use the {@link #getItem(Object ItemId) * getItem}method to fetch the Item. *

* *

* This functionality is optional. *

* * @return ID of the newly created Item, or null in case of a * failure * @throws UnsupportedOperationException * if adding an item without an explicit item ID is not * supported by the container */ public Object addItem() throws UnsupportedOperationException; /** * Removes the Item identified by ItemId from the Container. * *

* Containers that support filtering should also allow removing an item that * is currently filtered out. *

* *

* This functionality is optional. *

* * @param itemId * ID of the Item to remove * @return true if the operation succeeded, false * if not * @throws UnsupportedOperationException * if the container does not support removing individual items */ public boolean removeItem(Object itemId) throws UnsupportedOperationException; /** * Adds a new Property to all Items in the Container. The Property ID, data * type and default value of the new Property are given as parameters. *

* This functionality is optional. * * @param propertyId * ID of the Property * @param type * Data type of the new Property * @param defaultValue * The value all created Properties are initialized to * @return true if the operation succeeded, false * if not * @throws UnsupportedOperationException * if the container does not support explicitly adding container * properties */ public boolean addContainerProperty(Object propertyId, Class type, Object defaultValue) throws UnsupportedOperationException; /** * Removes a Property specified by the given Property ID from the Container. * Note that the Property will be removed from all Items in the Container. *

* This functionality is optional. * * @param propertyId * ID of the Property to remove * @return true if the operation succeeded, false * if not * @throws UnsupportedOperationException * if the container does not support removing container * properties */ public boolean removeContainerProperty(Object propertyId) throws UnsupportedOperationException; /** * Removes all Items from the Container. * *

* Note that Property ID and type information is preserved. This * functionality is optional. *

* * @return true if the operation succeeded, false * if not * @throws UnsupportedOperationException * if the container does not support removing all items */ public boolean removeAllItems() throws UnsupportedOperationException; /** * Interface for Container classes whose {@link Item}s can be traversed in * order. * *

* If the container is filtered or sorted, the traversal applies to the * filtered and sorted view. *

*

* The addItemAfter() methods should apply filters to the added * item after inserting it, possibly hiding it immediately. If the container * is being sorted, they may add items at the correct sorted position * instead of the given position. See also {@link Filterable} and * {@link Sortable} for more information. *

*/ public interface Ordered extends Container { /** * Gets the ID of the Item following the Item that corresponds to * itemId. If the given Item is the last or not found in * the Container, null is returned. * * @param itemId * ID of a visible Item in the Container * @return ID of the next visible Item or null */ public Object nextItemId(Object itemId); /** * Gets the ID of the Item preceding the Item that corresponds to * itemId. If the given Item is the first or not found in * the Container, null is returned. * * @param itemId * ID of a visible Item in the Container * @return ID of the previous visible Item or null */ public Object prevItemId(Object itemId); /** * Gets the ID of the first Item in the Container. * * @return ID of the first visible Item in the Container */ public Object firstItemId(); /** * Gets the ID of the last Item in the Container.. * * @return ID of the last visible Item in the Container */ public Object lastItemId(); /** * Tests if the Item corresponding to the given Item ID is the first * Item in the Container. * * @param itemId * ID of an Item in the Container * @return true if the Item is first visible item in the * Container, false if not */ public boolean isFirstId(Object itemId); /** * Tests if the Item corresponding to the given Item ID is the last Item * in the Container. * * @return true if the Item is last visible item in the * Container, false if not */ public boolean isLastId(Object itemId); /** * Adds a new item after the given item. *

* Adding an item after null item adds the item as first item of the * ordered container. *

* * @see Ordered Ordered: adding items in filtered or sorted containers * * @param previousItemId * Id of the visible item in ordered container after which to * insert the new item. * @return item id the the created new item or null if the operation * fails. * @throws UnsupportedOperationException * if the operation is not supported by the container */ public Object addItemAfter(Object previousItemId) throws UnsupportedOperationException; /** * Adds a new item after the given item. *

* Adding an item after null item adds the item as first item of the * ordered container. *

* * @see Ordered Ordered: adding items in filtered or sorted containers * * @param previousItemId * Id of the visible item in ordered container after which to * insert the new item. * @param newItemId * Id of the new item to be added. * @return new item or null if the operation fails. * @throws UnsupportedOperationException * if the operation is not supported by the container */ public Item addItemAfter(Object previousItemId, Object newItemId) throws UnsupportedOperationException; } /** * Interface for Container classes whose {@link Item}s can be sorted. *

* When an {@link Ordered} or {@link Indexed} container is sorted, all * relevant operations of these interfaces should only use the filtered and * sorted contents and the filtered indices to the container. Indices or * item identifiers in the public API refer to the visible view unless * otherwise stated. However, the addItem*() methods may add * items that will be filtered out after addition or moved to another * position based on sorting. *

*

* How sorting is performed when a {@link Hierarchical} container implements * {@link Sortable} is implementation specific and should be documented in * the implementing class. However, the recommended approach is sorting the * roots and the sets of children of each item separately. *

*

* Depending on the container type, sorting a container may permanently * change the internal order of items in the container. *

*/ public interface Sortable extends Ordered { /** * Sorts the container items. *

* Sorting a container can irreversibly change the order of its items or * only change the order temporarily, depending on the container. * * @param propertyId * Array of container property IDs, whose values are used to * sort the items in container as primary, secondary, ... * sorting criterion. All of the item IDs must be in the * collection returned by * {@link #getSortableContainerPropertyIds()} * @param ascending * Array of sorting order flags corresponding to each * property ID used in sorting. If this array is shorter than * propertyId array, ascending order is assumed for items * where the order is not specified. Use true to * sort in ascending order, false to use * descending order. */ void sort(Object[] propertyId, boolean[] ascending); /** * Gets the container property IDs which can be used to sort the items. * * @return the IDs of the properties that can be used for sorting the * container */ Collection getSortableContainerPropertyIds(); } /** * Interface for Container classes whose {@link Item}s can be accessed by * their position in the container. *

* If the container is filtered or sorted, all indices refer to the filtered * and sorted view. However, the addItemAt() methods may add * items that will be filtered out after addition or moved to another * position based on sorting. *

*/ public interface Indexed extends Ordered { /** * Gets the index of the Item corresponding to the itemId. The following * is true for the returned index: 0 <= index < size(), or * index = -1 if there is no visible item with that id in the container. * * @param itemId * ID of an Item in the Container * @return index of the Item, or -1 if (the filtered and sorted view of) * the Container does not include the Item */ public int indexOfId(Object itemId); /** * Get the item id for the item at the position given by * index. *

* * @param index * the index of the requested item id * @return the item id of the item at the given index * @throws IndexOutOfBoundsException * if index is outside the range of the * container. (i.e. * index < 0 || container.size()-1 < index * ) */ public Object getIdByIndex(int index); /** * Get numberOfItems consecutive item ids from the * container, starting with the item id at startIndex. *

* Implementations should return at most numberOfItems item * ids, but can contain less if the container has less items than * required to fulfill the request. The returned list must hence contain * all of the item ids from the range: *

* startIndex to * max(startIndex + (numberOfItems-1), container.size()-1). *

* For quick migration to new API see: * {@link ContainerHelpers#getItemIdsUsingGetIdByIndex(int, int, Indexed)} * * @param startIndex * the index for the first item which id to include * @param numberOfItems * the number of consecutive item ids to get from the given * start index, must be >= 0 * @return List containing the requested item ids or empty list if * numberOfItems == 0; not null * * @throws IllegalArgumentException * if numberOfItems is < 0 * @throws IndexOutOfBoundsException * if startIndex is outside the range of the * container. (i.e. * startIndex < 0 || container.size()-1 < startIndex * ) * * @since 7.0 */ public List getItemIds(int startIndex, int numberOfItems); /** * Adds a new item at given index (in the filtere

/*
 * 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.afp;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.EnumMap;

import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.fop.afp.AFPConstants;
import org.apache.fop.afp.AFPDataObjectInfo;
import org.apache.fop.afp.AFPEventProducer;
import org.apache.fop.afp.AFPResourceLevel;
import org.apache.fop.afp.AFPResourceLevelDefaults;
import org.apache.fop.afp.modca.triplets.MappingOptionTriplet;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.MimeConstants;
import org.apache.fop.apps.io.InternalResourceResolver;
import org.apache.fop.fonts.FontManager;
import org.apache.fop.render.RendererConfig;
import org.apache.fop.render.afp.AFPFontConfig.AFPFontInfoConfigParser;
import org.apache.fop.util.LogUtil;

import static org.apache.fop.render.afp.AFPRendererConfig.ImagesModeOptions.MODE_COLOR;
import static org.apache.fop.render.afp.AFPRendererConfig.ImagesModeOptions.MODE_GRAYSCALE;
import static org.apache.fop.render.afp.AFPRendererOption.DEFAULT_RESOURCE_LEVELS;
import static org.apache.fop.render.afp.AFPRendererOption.GOCA;
import static org.apache.fop.render.afp.AFPRendererOption.GOCA_TEXT;
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES;
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_DITHERING_QUALITY;
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_FS45;
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_JPEG;
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_MAPPING_OPTION;
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_MODE;
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_NATIVE;
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_WRAP_PSEG;
import static org.apache.fop.render.afp.AFPRendererOption.JPEG_ALLOW_JPEG_EMBEDDING;
import static org.apache.fop.render.afp.AFPRendererOption.JPEG_BITMAP_ENCODING_QUALITY;
import static org.apache.fop.render.afp.AFPRendererOption.LINE_WIDTH_CORRECTION;
import static org.apache.fop.render.afp.AFPRendererOption.RENDERER_RESOLUTION;
import static org.apache.fop.render.afp.AFPRendererOption.RESOURCE_GROUP_URI;
import static org.apache.fop.render.afp.AFPRendererOption.SHADING;

/**
 * The AFP renderer config object.
 */
public final class AFPRendererConfig implements RendererConfig {

    /**
     * An enumeration for the various images modes available to the AFP renderer.
     */
    public enum ImagesModeOptions {

        MODE_GRAYSCALE("b+w", "bits-per-pixel"),
        MODE_COLOR("color", "cmyk");

        private final String name;

        private final String modeAttribute;

        private ImagesModeOptions(String name, String modeAttribute) {
            this.name = name;
            this.modeAttribute = modeAttribute;
        }

        public String getName() {
            return name;
        }

        public String getModeAttribute() {
            return modeAttribute;
        }

        public static ImagesModeOptions forName(String name) {
            for (ImagesModeOptions option : values()) {
                if (option.name.equals(name)) {
                    return option;
                }
            }
            throw new IllegalArgumentException(name);
        }
    }

    private final EnumMap<AFPRendererOption, Object> params = new EnumMap<AFPRendererOption, Object>(AFPRendererOption.class);

    private final EnumMap<ImagesModeOptions, Object> imageModeParams
    = new EnumMap<ImagesModeOptions, Object>(ImagesModeOptions.class);

    private final AFPFontConfig fontConfig;

    private AFPRendererConfig(AFPFontConfig fontConfig) {
        this.fontConfig = fontConfig;
    }

    public AFPFontConfig getFontInfoConfig() {
        return fontConfig;
    }

    public Boolean isColorImages() {
        return getParam(IMAGES_MODE, Boolean.class);
    }

    public Boolean isCmykImagesSupported() {
        if (!isColorImages()) {
            throw new IllegalStateException();
        }
        return Boolean.class.cast(imageModeParams.get(MODE_COLOR));
    }

    public Integer getBitsPerPixel() {
        if (isColorImages()) {
            throw new IllegalStateException();
        }
        return Integer.class.cast(imageModeParams.get(MODE_GRAYSCALE));
    }

    public Float getDitheringQuality() {
        return getParam(IMAGES_DITHERING_QUALITY, Float.class);
    }

    public Boolean isNativeImagesSupported() {
        return getParam(IMAGES_NATIVE, Boolean.class);
    }

    public AFPShadingMode getShadingMode() {
        return getParam(SHADING, AFPShadingMode.class);
    }

    public Integer getResolution() {
        return getParam(RENDERER_RESOLUTION, Integer.class);
    }


    public URI getDefaultResourceGroupUri() {
        return getParam(RESOURCE_GROUP_URI, URI.class);
    }

    public AFPResourceLevelDefaults getResourceLevelDefaults() {
        return getParam(DEFAULT_RESOURCE_LEVELS, AFPResourceLevelDefaults.class);
    }

    public Boolean isWrapPseg() {
        return getParam(IMAGES_WRAP_PSEG, Boolean.class);
    }

    public Boolean isFs45() {
        return getParam(IMAGES_FS45, Boolean.class);
    }

    public Boolean allowJpegEmbedding() {
        return getParam(JPEG_ALLOW_JPEG_EMBEDDING, Boolean.class);
    }

    public Float getBitmapEncodingQuality() {
        return getParam(JPEG_BITMAP_ENCODING_QUALITY, Float.class);
    }

    public Float getLineWidthCorrection() {
        return getParam(LINE_WIDTH_CORRECTION, Float.class);
    }

    public Boolean isGocaEnabled() {
        return getParam(GOCA, Boolean.class);
    }

    public Boolean isStrokeGocaText() {
        return getParam(GOCA_TEXT, Boolean.class);
    }

    private <T> T getParam(AFPRendererOption options, Class<T> type) {
        assert options.getType().equals(type);
        return type.cast(params.get(options));
    }

    private <T> void setParam(AFPRendererOption option, T value) {
        assert option.getType().isInstance(value);
        params.put(option, value);
    }

    /**
     * The parser for AFP renderer specific data in the FOP conf.
     */
    public static final class AFPRendererConfigParser implements RendererConfigParser {

        private static final Log LOG = LogFactory.getLog(AFPRendererConfigParser.class);

        /** {@inheritDoc} */
        public AFPRendererConfig build(FOUserAgent userAgent, Configuration cfg) throws FOPException {
            boolean strict = userAgent != null ? userAgent.validateUserConfigStrictly() : false;
            AFPRendererConfig config = null;
            AFPEventProducer eventProducer = AFPEventProducer.Provider.get(userAgent.getEventBroadcaster());
            try {
                config = new ParserHelper(cfg, userAgent.getFontManager(), strict, eventProducer).config;
            } catch (ConfigurationException e) {
                LogUtil.handleException(LOG, e, strict);
            }
            return config;
        }

        /** {@inheritDoc} */
        public String getMimeType() {
            return MimeConstants.MIME_AFP;
        }
    }

    private static final class ParserHelper {

        private static final Log LOG = LogFactory.getLog(ParserHelper.class);

        private final AFPRendererConfig config;

        private final boolean strict;

        private final Configuration cfg;

        private ParserHelper(Configuration cfg, FontManager fontManager, boolean strict,
                AFPEventProducer eventProducer)
                        throws ConfigurationException, FOPException {
            this.cfg = cfg;
            this.strict = strict;
            if (cfg != null) {
                config = new AFPRendererConfig(new AFPFontInfoConfigParser().parse(cfg,
                        fontManager, strict, eventProducer));
                configure();
            } else {
                config = new AFPRendererConfig(new AFPFontInfoConfigParser().getEmptyConfig());
            }
        }

        private void configure() throws ConfigurationException, FOPException {
            configureImages();
            setParam(SHADING, AFPShadingMode.getValueOf(
                    cfg.getChild(SHADING.getName()).getValue(AFPShadingMode.COLOR.getName())));
            Configuration rendererResolutionCfg = cfg.getChild(RENDERER_RESOLUTION.getName(), false);
            setParam(RENDERER_RESOLUTION, rendererResolutionCfg == null ? 240
                    : rendererResolutionCfg.getValueAsInteger(240));
            Configuration lineWidthCorrectionCfg = cfg.getChild(LINE_WIDTH_CORRECTION.getName(),
                    false);
            setParam(LINE_WIDTH_CORRECTION, lineWidthCorrectionCfg != null
                    ? lineWidthCorrectionCfg.getValueAsFloat()
                    : AFPConstants.LINE_WIDTH_CORRECTION);
            Configuration gocaCfg = cfg.getChild(GOCA.getName());
            boolean gocaEnabled = gocaCfg.getAttributeAsBoolean("enabled", true);
            setParam(GOCA, gocaEnabled);
            String strokeGocaText = gocaCfg.getAttribute(GOCA_TEXT.getName(), "default");
            setParam(GOCA_TEXT, "stroke".equalsIgnoreCase(strokeGocaText)
                            || "shapes".equalsIgnoreCase(strokeGocaText));
            //TODO remove
            createResourceGroupFile();
            createResourceLevel();
        }

        private void setParam(AFPRendererOption option, Object value) {
            config.setParam(option, value);
        }

        private void configureImages() throws ConfigurationException, FOPException {
            Configuration imagesCfg = cfg.getChild(IMAGES.getName());
            ImagesModeOptions imagesMode = ImagesModeOptions.forName(imagesCfg.getAttribute(
                    IMAGES_MODE.getName(), MODE_GRAYSCALE.getName()));
            boolean colorImages = MODE_COLOR == imagesMode;
            setParam(IMAGES_MODE, colorImages);
            if (colorImages) {
                config.imageModeParams.put(MODE_COLOR, imagesCfg
                        .getAttributeAsBoolean(imagesMode.getModeAttribute(), false));
            } else {
                config.imageModeParams.put(MODE_GRAYSCALE,
                        imagesCfg.getAttributeAsInteger(imagesMode.getModeAttribute(), 8));
            }
            String dithering = imagesCfg.getAttribute(AFPRendererOption.IMAGES_DITHERING_QUALITY.getName(), "medium");
            float dq;
            if (dithering.startsWith("min")) {
                dq = 0.0f;
            } else if (dithering.startsWith("max")) {
                dq = 1.0f;
            } else {
                try {
                    dq = Float.parseFloat(dithering);
                } catch (NumberFormatException nfe) {
                    //Default value
                    dq = 0.5f;
                }
            }
            setParam(IMAGES_DITHERING_QUALITY, dq);
            setParam(IMAGES_NATIVE, imagesCfg.getAttributeAsBoolean(IMAGES_NATIVE.getName(), false));
            setParam(IMAGES_WRAP_PSEG,
                    imagesCfg.getAttributeAsBoolean(IMAGES_WRAP_PSEG.getName(), false));
            setParam(IMAGES_FS45, imagesCfg.getAttributeAsBoolean(IMAGES_FS45.getName(), false));
            if ("scale-to-fit".equals(imagesCfg.getAttribute(IMAGES_MAPPING_OPTION.getName(), null))) {
                setParam(IMAGES_MAPPING_OPTION, MappingOptionTriplet.SCALE_TO_FILL);
            } else {
                setParam(IMAGES_MAPPING_OPTION, AFPDataObjectInfo.DEFAULT_MAPPING_OPTION);
            }
            configureJpegImages(imagesCfg);
        }

        private void configureJpegImages(Configuration imagesCfg) {
            Configuration jpegConfig = imagesCfg.getChild(IMAGES_JPEG.getName());
            float bitmapEncodingQuality = 1.0f;
            boolean allowJpegEmbedding = false;
            if (jpegConfig != null) {
                allowJpegEmbedding = jpegConfig.getAttributeAsBoolean(
                        JPEG_ALLOW_JPEG_EMBEDDING.getName(),
                        false);
                String bitmapEncodingQualityStr = jpegConfig.getAttribute(
                        JPEG_BITMAP_ENCODING_QUALITY.getName(), null);
                if (bitmapEncodingQualityStr != null) {
                    try {
                        bitmapEncodingQuality = Float.parseFloat(bitmapEncodingQualityStr);
                    } catch (NumberFormatException nfe) {
                        //ignore and leave the default above
                    }
                }
            }
            setParam(JPEG_BITMAP_ENCODING_QUALITY, bitmapEncodingQuality);
            setParam(JPEG_ALLOW_JPEG_EMBEDDING, allowJpegEmbedding);
        }

        private void createResourceGroupFile() throws FOPException {
            try {
                Configuration resourceGroupUriCfg = cfg.getChild(RESOURCE_GROUP_URI.getName(), false);
                if (resourceGroupUriCfg != null) {
                    URI resourceGroupUri = InternalResourceResolver.cleanURI(resourceGroupUriCfg.getValue());
                    setParam(RESOURCE_GROUP_URI, resourceGroupUri);
                }
            } catch (ConfigurationException e) {
                LogUtil.handleException(LOG, e, strict);
            } catch (URISyntaxException use) {
                LogUtil.handleException(LOG, use, strict);
            }
        }

        private void createResourceLevel() throws FOPException {
            Configuration defaultResourceLevelCfg = cfg.getChild(DEFAULT_RESOURCE_LEVELS.getName(), false);
            if (defaultResourceLevelCfg != null) {
                AFPResourceLevelDefaults defaults = new AFPResourceLevelDefaults();
                String[] types = defaultResourceLevelCfg.getAttributeNames();
                for (int i = 0, c = types.length; i < c; i++) {
                    String type = types[i];
                    try {
                        String level = defaultResourceLevelCfg.getAttribute(type);
                        defaults.setDefaultResourceLevel(type, AFPResourceLevel.valueOf(level));
                    } catch (IllegalArgumentException iae) {
                        LogUtil.handleException(LOG, iae, strict);
                    } catch (ConfigurationException e) {
                        LogUtil.handleException(LOG, e, strict);
                    }
                }
                setParam(DEFAULT_RESOURCE_LEVELS, defaults);
            }
        }
    }
}