org.apache.fop.render.afp.AFPDocumentHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.apache.fop Show documentation
Show all versions of org.apache.fop Show documentation
The core maven build properties
The 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: AFPDocumentHandler.java 1891052 2021-06-26 06:39:20Z ssteiner $ */
package org.apache.fop.render.afp;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.geom.AffineTransform;
import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.fop.afp.AFPDitheredRectanglePainter;
import org.apache.fop.afp.AFPPaintingState;
import org.apache.fop.afp.AFPRectanglePainter;
import org.apache.fop.afp.AFPResourceLevelDefaults;
import org.apache.fop.afp.AFPResourceManager;
import org.apache.fop.afp.AFPUnitConverter;
import org.apache.fop.afp.AbstractAFPPainter;
import org.apache.fop.afp.DataStream;
import org.apache.fop.afp.fonts.AFPFontCollection;
import org.apache.fop.afp.fonts.AFPPageFonts;
import org.apache.fop.afp.modca.ResourceObject;
import org.apache.fop.afp.util.AFPResourceAccessor;
import org.apache.fop.apps.MimeConstants;
import org.apache.fop.fonts.FontCollection;
import org.apache.fop.fonts.FontEventAdapter;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontManager;
import org.apache.fop.render.afp.AFPRendererConfig.AFPRendererConfigParser;
import org.apache.fop.render.afp.extensions.AFPElementMapping;
import org.apache.fop.render.afp.extensions.AFPIncludeFormMap;
import org.apache.fop.render.afp.extensions.AFPInvokeMediumMap;
import org.apache.fop.render.afp.extensions.AFPPageOverlay;
import org.apache.fop.render.afp.extensions.AFPPageSegmentElement;
import org.apache.fop.render.afp.extensions.AFPPageSetup;
import org.apache.fop.render.afp.extensions.ExtensionPlacement;
import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
import org.apache.fop.render.intermediate.IFContext;
import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
import org.apache.fop.render.intermediate.IFException;
import org.apache.fop.render.intermediate.IFPainter;
/**
* {@link org.apache.fop.render.intermediate.IFDocumentHandler} implementation that produces AFP
* (MO:DCA).
*/
public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
implements AFPCustomizable {
//** logging instance */
//private static Log log = LogFactory.getLog(AFPDocumentHandler.class);
/** the resource manager */
private AFPResourceManager resourceManager;
/** the painting state */
private final AFPPaintingState paintingState;
/** unit converter */
private final AFPUnitConverter unitConv;
/** the AFP datastream */
private DataStream dataStream;
/** the map of page segments */
private Map pageSegmentMap
= new java.util.HashMap();
// Rounded corners are cached at the document level
private Map roundedCornerNameCache
= new HashMap();
private int roundedCornerCount;
private static enum Location {
ELSEWHERE, IN_DOCUMENT_HEADER, FOLLOWING_PAGE_SEQUENCE, IN_PAGE_HEADER
}
private Location location = Location.ELSEWHERE;
/** temporary holds extensions that have to be deferred until the end of the page-sequence */
private List deferredPageSequenceExtensions
= new java.util.LinkedList();
/** the shading mode for filled rectangles */
private AFPShadingMode shadingMode = AFPShadingMode.COLOR;
/**
* Default constructor.
*/
public AFPDocumentHandler(IFContext context) {
super(context);
this.resourceManager = new AFPResourceManager(context.getUserAgent().getResourceResolver());
this.paintingState = new AFPPaintingState();
this.unitConv = paintingState.getUnitConverter();
}
/** {@inheritDoc} */
public boolean supportsPagesOutOfOrder() {
return false;
}
/** {@inheritDoc} */
public String getMimeType() {
return MimeConstants.MIME_AFP;
}
/** {@inheritDoc} */
public IFDocumentHandlerConfigurator getConfigurator() {
return new AFPRendererConfigurator(getUserAgent(), new AFPRendererConfigParser());
}
/** {@inheritDoc} */
@Override
public void setDefaultFontInfo(FontInfo fontInfo) {
FontManager fontManager = getUserAgent().getFontManager();
FontCollection[] fontCollections = new FontCollection[] {
new AFPFontCollection(getUserAgent().getEventBroadcaster(), null)
};
FontInfo fi = (fontInfo != null ? fontInfo : new FontInfo());
fi.setEventListener(new FontEventAdapter(getUserAgent().getEventBroadcaster()));
fontManager.setup(fi, fontCollections);
setFontInfo(fi);
}
AFPPaintingState getPaintingState() {
return this.paintingState;
}
DataStream getDataStream() {
return this.dataStream;
}
AFPResourceManager getResourceManager() {
return this.resourceManager;
}
AbstractAFPPainter createRectanglePainter() {
if (AFPShadingMode.DITHERED.equals(this.shadingMode)) {
return new AFPDitheredRectanglePainter(
getPaintingState(), getDataStream(), getResourceManager());
} else {
return new AFPRectanglePainter(
getPaintingState(), getDataStream());
}
}
/** {@inheritDoc} */
@Override
public void startDocument() throws IFException {
super.startDocument();
try {
paintingState.setColor(Color.WHITE);
this.dataStream = resourceManager.createDataStream(paintingState, outputStream);
this.dataStream.startDocument();
} catch (IOException e) {
throw new IFException("I/O error in startDocument()", e);
}
}
/** {@inheritDoc} */
@Override
public void startDocumentHeader() throws IFException {
super.startDocumentHeader();
this.location = Location.IN_DOCUMENT_HEADER;
}
/** {@inheritDoc} */
@Override
public void endDocumentHeader() throws IFException {
super.endDocumentHeader();
this.location = Location.ELSEWHERE;
}
/** {@inheritDoc} */
@Override
public void endDocument() throws IFException {
try {
this.dataStream.endDocument();
this.dataStream = null;
this.resourceManager.writeToStream();
this.resourceManager = null;
} catch (IOException ioe) {
throw new IFException("I/O error in endDocument()", ioe);
}
super.endDocument();
}
/** {@inheritDoc} */
public void startPageSequence(String id) throws IFException {
try {
if (!"false".equals(getContext().getForeignAttribute(AFPElementMapping.PAGE_GROUP))) {
dataStream.startPageGroup();
}
} catch (IOException ioe) {
throw new IFException("I/O error in startPageSequence()", ioe);
}
this.location = Location.FOLLOWING_PAGE_SEQUENCE;
}
/** {@inheritDoc} */
public void endPageSequence() throws IFException {
try {
//Process deferred page-sequence-level extensions
Iterator iter = this.deferredPageSequenceExtensions.iterator();
while (iter.hasNext()) {
AFPPageSetup aps = iter.next();
iter.remove();
if (AFPElementMapping.NO_OPERATION.equals(aps.getElementName())) {
handleNOP(aps);
} else {
throw new UnsupportedOperationException("Don't know how to handle " + aps);
}
}
//End page sequence
dataStream.endPageGroup();
} catch (IOException ioe) {
throw new IFException("I/O error in endPageSequence()", ioe);
}
this.location = Location.ELSEWHERE;
}
/**
* Returns the base AFP transform
*
* @return the base AFP transform
*/
private AffineTransform getBaseTransform() {
AffineTransform baseTransform = new AffineTransform();
double scale = unitConv.mpt2units(1);
baseTransform.scale(scale, scale);
return baseTransform;
}
/** {@inheritDoc} */
public void startPage(int index, String name, String pageMasterName, Dimension size)
throws IFException {
this.location = Location.ELSEWHERE;
paintingState.clear();
AffineTransform baseTransform = getBaseTransform();
paintingState.concatenate(baseTransform);
int pageWidth = Math.round(unitConv.mpt2units(size.width));
paintingState.setPageWidth(pageWidth);
int pageHeight = Math.round(unitConv.mpt2units(size.height));
paintingState.setPageHeight(pageHeight);
int pageRotation = paintingState.getPageRotation();
int resolution = paintingState.getResolution();
dataStream.startPage(pageWidth, pageHeight, pageRotation,
resolution, resolution);
}
/** {@inheritDoc} */
@Override
public void startPageHeader() throws IFException {
super.startPageHeader();
this.location = Location.IN_PAGE_HEADER;
}
/** {@inheritDoc} */
@Override
public void endPageHeader() throws IFException {
this.location = Location.ELSEWHERE;
super.endPageHeader();
}
/** {@inheritDoc} */
public IFPainter startPageContent() throws IFException {
return new AFPPainter(this);
}
/** {@inheritDoc} */
public void endPageContent() throws IFException {
}
/** {@inheritDoc} */
public void endPage() throws IFException {
try {
AFPPageFonts pageFonts = paintingState.getPageFonts();
if (pageFonts != null && !pageFonts.isEmpty()) {
dataStream.addFontsToCurrentPage(pageFonts);
}
dataStream.endPage();
} catch (IOException ioe) {
throw new IFException("I/O error in endPage()", ioe);
}
}
/** {@inheritDoc} */
public void handleExtensionObject(Object extension) throws IFException {
if (extension instanceof AFPPageSetup) {
AFPPageSetup aps = (AFPPageSetup)extension;
String element = aps.getElementName();
if (AFPElementMapping.TAG_LOGICAL_ELEMENT.equals(element)) {
switch (this.location) {
case FOLLOWING_PAGE_SEQUENCE:
case IN_PAGE_HEADER:
String name = aps.getName();
String value = aps.getValue();
int encoding = aps.getEncoding();
dataStream.createTagLogicalElement(name, value, encoding);
break;
default:
throw new IFException(
"TLE extension must be in the page header or between page-sequence"
+ " and the first page: " + aps, null);
}
} else if (AFPElementMapping.NO_OPERATION.equals(element)) {
switch (this.location) {
case FOLLOWING_PAGE_SEQUENCE:
if (aps.getPlacement() == ExtensionPlacement.BEFORE_END) {
this.deferredPageSequenceExtensions.add(aps);
break;
}
case IN_DOCUMENT_HEADER:
case IN_PAGE_HEADER:
handleNOP(aps);
break;
default:
throw new IFException(
"NOP extension must be in the document header, the page header"
+ " or between page-sequence"
+ " and the first page: " + aps, null);
}
} else {
if (this.location != Location.IN_PAGE_HEADER) {
throw new IFException(
"AFP page setup extension encountered outside the page header: " + aps,
null);
}
if (AFPElementMapping.INCLUDE_PAGE_SEGMENT.equals(element)) {
AFPPageSegmentElement.AFPPageSegmentSetup apse
= (AFPPageSegmentElement.AFPPageSegmentSetup)aps;
String name = apse.getName();
String source = apse.getValue();
String uri = apse.getResourceSrc();
pageSegmentMap.put(source, new PageSegmentDescriptor(name, uri));
}
}
} else if (extension instanceof AFPPageOverlay) {
AFPPageOverlay ipo = (AFPPageOverlay)extension;
if (this.location != Location.IN_PAGE_HEADER) {
throw new IFException(
"AFP page overlay extension encountered outside the page header: " + ipo,
null);
}
String overlay = ipo.getName();
if (overlay != null) {
dataStream.createIncludePageOverlay(overlay, ipo.getX(), ipo.getY());
}
} else if (extension instanceof AFPInvokeMediumMap) {
if (this.location != Location.FOLLOWING_PAGE_SEQUENCE
&& this.location != Location.IN_PAGE_HEADER) {
throw new IFException(
"AFP IMM extension must be between page-sequence"
+ " and the first page or child of page-header: "
+ extension, null);
}
AFPInvokeMediumMap imm = (AFPInvokeMediumMap)extension;
String mediumMap = imm.getName();
if (mediumMap != null) {
dataStream.createInvokeMediumMap(mediumMap);
}
} else if (extension instanceof AFPIncludeFormMap) {
AFPIncludeFormMap formMap = (AFPIncludeFormMap)extension;
AFPResourceAccessor accessor = new AFPResourceAccessor(
getUserAgent().getResourceResolver());
try {
getResourceManager().createIncludedResource(formMap.getName(),
formMap.getSrc(), accessor,
ResourceObject.TYPE_FORMDEF, false, null);
} catch (IOException ioe) {
throw new IFException(
"I/O error while embedding form map resource: " + formMap.getName(), ioe);
}
}
}
/**
* Corner images can be reused by storing at the document level in the AFP
* The cache is used to map cahced images to caller generated descriptions of the corner
* @param cornerKey caller's identifier for the corner
* @return document id of the corner image
*/
public String cacheRoundedCorner(String cornerKey) {
// Make a unique id
StringBuffer idBuilder = new StringBuffer("RC");
String tmp = Integer.toHexString(roundedCornerCount).toUpperCase(Locale.ENGLISH);
if (tmp.length() > 6) {
//Will never happen
//log.error("Rounded corners cache capacity exceeded");
//We should get a visual clue
roundedCornerCount = 0;
tmp = "000000";
} else if (tmp.length() < 6) {
for (int i = 0; i < 6 - tmp.length(); i++) {
idBuilder.append("0");
}
idBuilder.append(tmp);
}
roundedCornerCount++;
String id = idBuilder.toString();
//cache the corner id
roundedCornerNameCache.put(cornerKey, id);
return id;
}
/**
* This method returns the an id that identifies a cached corner or null if non existent
* @param cornerKey caller's identifier for the corner
* @return document id of the corner image
*/
public String getCachedRoundedCorner(String cornerKey) {
return roundedCornerNameCache.get(cornerKey);
}
private void handleNOP(AFPPageSetup nop) {
String content = nop.getContent();
if (content != null) {
dataStream.createNoOperation(content);
}
}
// ---=== AFPCustomizable ===---
/** {@inheritDoc} */
public void setBitsPerPixel(int bitsPerPixel) {
paintingState.setBitsPerPixel(bitsPerPixel);
}
/** {@inheritDoc} */
public void setColorImages(boolean colorImages) {
paintingState.setColorImages(colorImages);
}
/** {@inheritDoc} */
public void setNativeImagesSupported(boolean nativeImages) {
paintingState.setNativeImagesSupported(nativeImages);
}
/** {@inheritDoc} */
public void setCMYKImagesSupported(boolean value) {
paintingState.setCMYKImagesSupported(value);
}
/** {@inheritDoc} */
public void setDitheringQuality(float quality) {
this.paintingState.setDitheringQuality(quality);
}
/** {@inheritDoc} */
public void setBitmapEncodingQuality(float quality) {
this.paintingState.setBitmapEncodingQuality(quality);
}
/** {@inheritDoc} */
public void setShadingMode(AFPShadingMode shadingMode) {
this.shadingMode = shadingMode;
}
/** {@inheritDoc} */
public void setResolution(int resolution) {
paintingState.setResolution(resolution);
}
/** {@inheritDoc} */
public void setLineWidthCorrection(float correction) {
paintingState.setLineWidthCorrection(correction);
}
/** {@inheritDoc} */
public int getResolution() {
return paintingState.getResolution();
}
/** {@inheritDoc} */
public void setGOCAEnabled(boolean enabled) {
this.paintingState.setGOCAEnabled(enabled);
}
/** {@inheritDoc} */
public boolean isGOCAEnabled() {
return this.paintingState.isGOCAEnabled();
}
/** {@inheritDoc} */
public void setStrokeGOCAText(boolean stroke) {
this.paintingState.setStrokeGOCAText(stroke);
}
/** {@inheritDoc} */
public boolean isStrokeGOCAText() {
return this.paintingState.isStrokeGOCAText();
}
/** {@inheritDoc} */
public void setWrapPSeg(boolean pSeg) {
paintingState.setWrapPSeg(pSeg);
}
public void setWrapGocaPSeg(boolean pSeg) {
paintingState.setWrapGocaPSeg(pSeg);
}
/** {@inheritDoc} */
public void setFS45(boolean fs45) {
paintingState.setFS45(fs45);
}
/** {@inheritDoc} */
public boolean getWrapPSeg() {
return paintingState.getWrapPSeg();
}
/** {@inheritDoc} */
public boolean getFS45() {
return paintingState.getFS45();
}
public void setDefaultResourceGroupUri(URI uri) {
resourceManager.setDefaultResourceGroupUri(uri);
}
/** {@inheritDoc} */
public void setResourceLevelDefaults(AFPResourceLevelDefaults defaults) {
resourceManager.setResourceLevelDefaults(defaults);
}
/**
* Returns the page segment descriptor for a given URI if it actually represents a page segment.
* Otherwise, it just returns null.
* @param uri the URI that identifies the page segment
* @return the page segment descriptor or null if there's no page segment for the given URI
*/
PageSegmentDescriptor getPageSegmentNameFor(String uri) {
return pageSegmentMap.get(uri);
}
/** {@inheritDoc} */
public void canEmbedJpeg(boolean canEmbed) {
paintingState.setCanEmbedJpeg(canEmbed);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy