https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk ........ r607036 | jeremias | 2007-12-27 11:51:11 +0100 (Do, 27 Dez 2007) | 1 line Don't just exit with no error message if the document contains no content. Pretty irritating if it does so. ........ r608812 | acumiskey | 2008-01-04 13:14:33 +0100 (Fr, 04 Jan 2008) | 3 lines The fonts variable would have always had an empty Configuration node (non-null value) even if a <fonts/> wasn't present in the fop configuration. ........ r609567 | jeremias | 2008-01-07 11:52:09 +0100 (Mo, 07 Jan 2008) | 4 lines Reenabled documentation for fox:destination. Enabled intermediate format functionality for fox:destination. Added a test case to check fox:destination. Deprecated FOP's XMLizable in favor of the XML Graphics Commons variant (and extend that variant). ........ r609627 | jeremias | 2008-01-07 16:06:24 +0100 (Mo, 07 Jan 2008) | 5 lines Bugzilla #44176: Support for custom fonts in Java2DRenderer and derived renderers. Submitted by: Patrick Jaromin <patrick.at.jgsullivan.dot.com> Patch modified slightly by jeremias. ........ r610020 | acumiskey | 2008-01-08 16:27:02 +0100 (Di, 08 Jan 2008) | 2 lines cleaned up ........ r610021 | acumiskey | 2008-01-08 16:28:56 +0100 (Di, 08 Jan 2008) | 2 lines Appears to be unused/referenced and superceeded by PageGroup ........ r610022 | acumiskey | 2008-01-08 16:34:07 +0100 (Di, 08 Jan 2008) | 2 lines cleaned up ........ r610023 | acumiskey | 2008-01-08 16:35:18 +0100 (Di, 08 Jan 2008) | 2 lines fixed javadoc ........ r610337 | vhennebert | 2008-01-09 12:02:08 +0100 (Mi, 09 Jan 2008) | 2 lines Fixed checkstyle issues: tabs and trailing spaces ........ r610355 | vhennebert | 2008-01-09 13:01:21 +0100 (Mi, 09 Jan 2008) | 2 lines keep-together is an inherited property ........ r610420 | acumiskey | 2008-01-09 16:40:25 +0100 (Mi, 09 Jan 2008) | 2 lines cleanup ........ r610704 | jeremias | 2008-01-10 08:38:47 +0100 (Do, 10 Jan 2008) | 1 line Added basic support for PDF page labels. ........ r610739 | jeremias | 2008-01-10 11:13:21 +0100 (Do, 10 Jan 2008) | 1 line PostScript output now generates the bounding box DSC comments for the whole document. ........ r610821 | vhennebert | 2008-01-10 16:53:20 +0100 (Do, 10 Jan 2008) | 4 lines - renamed variables for clarity - moved the computation of a cell's content length in PrimaryGridUnit - better javadoc for getHeight method in EffRow ........ r610848 | vhennebert | 2008-01-10 18:41:52 +0100 (Do, 10 Jan 2008) | 2 lines No need to check if the end of the cell is reached when creating the areas for a row ........ r610853 | vhennebert | 2008-01-10 18:54:16 +0100 (Do, 10 Jan 2008) | 2 lines Simplified addAreasAndFlushRow ........ r610886 | vhennebert | 2008-01-10 20:23:56 +0100 (Do, 10 Jan 2008) | 2 lines Removed endPart() method and moved its content into addAreasAndFlushRow() ........ r610891 | vhennebert | 2008-01-10 20:34:13 +0100 (Do, 10 Jan 2008) | 2 lines Removed accumulatedBPD which is redundant with yoffset ........ r610893 | vhennebert | 2008-01-10 20:35:24 +0100 (Do, 10 Jan 2008) | 2 lines The return value of addAreasAndFlushRow is never used, changed it to void ........ r610905 | vhennebert | 2008-01-10 20:57:29 +0100 (Do, 10 Jan 2008) | 2 lines Renamed lastRow into currentRow and yoffset into currentRowOffset ........ r611114 | jeremias | 2008-01-11 10:04:28 +0100 (Fr, 11 Jan 2008) | 2 lines Bugfix: Some string objects were not encrypted (for example in named destinations) I had to refactor the PDF library a little bit but since it only affects the inner API it shouldn't be a problem that I removed some methods which caused trouble because a didn't think about encryption when I worked on the PDF library last year. ........ git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_ImagePackageRedesign@611120 13f79535-47bb-0310-9956-ffa450edef68Temp_ImagePackageRedesign
@@ -63,8 +63,7 @@ | |||
</section> | |||
<section id="named-destinations"> | |||
<title>Anchors or Named Destinations</title> | |||
<p>This extension element hasn't been reimplemented for the redesigned code, yet.</p> | |||
<!--p>Use the fox:destination element to define "named destinations" inside a PDF document. | |||
<p>Use the fox:destination element to define "named destinations" inside a PDF document. | |||
These are useful as fragment identifiers, e.g. "http://server/document.pdf#anchor-name". | |||
fox:destination elements can be placed almost anywhere in the fo document, including a child of | |||
root, a block-level element, or an inline-level element. | |||
@@ -77,7 +76,7 @@ PDF document. The fox:destination simply gives that view an independent name. | |||
<fo:block id="table-of-contents">Table of Contents</fo:block>]]></source> | |||
<warning>It is possible that in some future release of FOP, <em>all </em>elements with | |||
"id" attributes will generate named-destinations, which will eliminate the need for | |||
fox:destination.</warning--> | |||
fox:destination.</warning> | |||
</section> | |||
<section id="table-continue-label"> | |||
<title>Table Continuation Label</title> | |||
@@ -136,7 +135,7 @@ to following pages. Here is an example of FO code creating such a table-header:< | |||
</p> | |||
<p> | |||
<code> | |||
(layout-master-set, declarations?, bookmark-tree?, (page-sequence|page-sequence-wrapper|fox:external-document)+) | |||
(layout-master-set, declarations?, bookmark-tree?, (page-sequence|page-sequence-wrapper|fox:external-document|fox:destination)+) | |||
</code> | |||
</p> | |||
<section> |
@@ -184,6 +184,7 @@ public class AreaTreeParser { | |||
makers.put("foreignObject", new ForeignObjectMaker()); | |||
makers.put("bookmarkTree", new BookmarkTreeMaker()); | |||
makers.put("bookmark", new BookmarkMaker()); | |||
makers.put("destination", new DestinationMaker()); | |||
} | |||
private static Rectangle2D parseRect(String rect) { | |||
@@ -927,6 +928,26 @@ public class AreaTreeParser { | |||
} | |||
} | |||
private class DestinationMaker extends AbstractMaker { | |||
public void startElement(Attributes attributes) { | |||
String[] linkdata | |||
= InternalLink.parseXMLAttribute(lastAttributes.getValue("internal-link")); | |||
PageViewport pv = (PageViewport) pageViewportsByKey.get(linkdata[0]); | |||
DestinationData dest = new DestinationData(linkdata[1]); | |||
List pages = new java.util.ArrayList(); | |||
pages.add(pv); | |||
dest.resolveIDRef(linkdata[1], pages); | |||
areaStack.push(dest); | |||
} | |||
public void endElement() { | |||
Object tos = areaStack.pop(); | |||
assertObjectOfClass(tos, DestinationData.class); | |||
treeModel.handleOffDocumentItem((DestinationData) tos); | |||
} | |||
} | |||
// ==================================================================== | |||
@@ -22,7 +22,6 @@ package org.apache.fop.area; | |||
import java.util.List; | |||
import org.apache.fop.fo.extensions.destination.Destination; | |||
import org.apache.fop.area.PageViewport; | |||
/** | |||
* An instance of this class is named destination from fox:destination | |||
*/ | |||
@@ -46,10 +45,18 @@ public class DestinationData extends AbstractOffDocumentItem implements Resolvab | |||
* @param destination the fo:bookmark object | |||
*/ | |||
public DestinationData(Destination destination) { | |||
idRef = destination.getInternalDestination(); | |||
idRefs = new String[] {idRef}; | |||
this(destination.getInternalDestination()); | |||
} | |||
/** | |||
* Create a new named destination. | |||
* @param idRef the id reference of the destination | |||
*/ | |||
public DestinationData(String idRef) { | |||
this.idRef = idRef; | |||
this.idRefs = new String[] {idRef}; | |||
} | |||
/** | |||
* Get the idref for this destination | |||
* | |||
@@ -99,9 +106,7 @@ public class DestinationData extends AbstractOffDocumentItem implements Resolvab | |||
// TODO get rect area of id on page | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
/** {@inheritDoc} */ | |||
public String getName() { | |||
return "Destination"; | |||
} |
@@ -1802,7 +1802,7 @@ public final class FOPropertyMapping implements Constants { | |||
// keep-together | |||
m = new KeepProperty.Maker(PR_KEEP_TOGETHER); | |||
m.useGeneric(genericKeep); | |||
m.setInherited(false); | |||
m.setInherited(true); | |||
m.setDefault("auto"); | |||
m.addShorthand(s_generics[PR_PAGE_BREAK_INSIDE]); | |||
addPropertyMaker("keep-together", m); |
@@ -81,6 +81,7 @@ public class FOTreeBuilder extends DefaultHandler { | |||
private FOUserAgent userAgent; | |||
private boolean used = false; | |||
private boolean empty = true; | |||
private int depth; | |||
@@ -147,6 +148,7 @@ public class FOTreeBuilder extends DefaultHandler { | |||
+ " Please instantiate a new instance."); | |||
} | |||
used = true; | |||
empty = true; | |||
rootFObj = null; // allows FOTreeBuilder to be reused | |||
if (log.isDebugEnabled()) { | |||
log.debug("Building formatting object tree"); | |||
@@ -162,6 +164,10 @@ public class FOTreeBuilder extends DefaultHandler { | |||
*/ | |||
public void endDocument() throws SAXException { | |||
this.delegate.endDocument(); | |||
if (this.rootFObj == null && empty) { | |||
throw new ValidationException( | |||
"Document is empty (something might be wrong with your XSLT stylesheet)."); | |||
} | |||
rootFObj = null; | |||
if (log.isDebugEnabled()) { | |||
log.debug("Parsing of document complete"); | |||
@@ -280,6 +286,7 @@ public class FOTreeBuilder extends DefaultHandler { | |||
// Check to ensure first node encountered is an fo:root | |||
if (rootFObj == null) { | |||
empty = false; | |||
if (!namespaceURI.equals(FOElementMapping.URI) | |||
|| !localName.equals("root")) { | |||
throw new ValidationException( |
@@ -80,13 +80,20 @@ public class EffRow { | |||
return getGridUnit(0).getRow(); | |||
} | |||
/** @return the calculated height for this EffRow. */ | |||
/** | |||
* Returns the calculated height for this EffRow, including the cells' | |||
* bpds/paddings/borders, and the table's border-separation. | |||
* | |||
* @return the row's height | |||
*/ | |||
public MinOptMax getHeight() { | |||
return this.height; | |||
} | |||
/** | |||
* Sets the calculated height for this EffRow. | |||
* Sets the calculated height for this EffRow, including everything (cells' bpds, | |||
* paddings, borders, and border-separation). | |||
* | |||
* @param mom the calculated height | |||
*/ | |||
public void setHeight(MinOptMax mom) { |
@@ -24,6 +24,7 @@ import java.util.List; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.layoutmgr.ElementListUtils; | |||
import org.apache.fop.layoutmgr.table.TableCellLayoutManager; | |||
/** | |||
@@ -142,13 +143,11 @@ public class PrimaryGridUnit extends GridUnit { | |||
return getHalfMaxBeforeBorderWidth() + getHalfMaxAfterBorderWidth(); | |||
} | |||
/** @param value The length of the cell content to remember. */ | |||
public void setContentLength(int value) { | |||
this.contentLength = value; | |||
} | |||
/** @return the length of the cell content. */ | |||
public int getContentLength() { | |||
if (contentLength < 0) { | |||
contentLength = ElementListUtils.calcContentLength(elements); | |||
} | |||
return contentLength; | |||
} | |||
@@ -148,9 +148,9 @@ class RowGroupLayoutManager { | |||
TableRow tableRow = null; | |||
// The row's minimum content height; 0 if the row's height is auto, otherwise | |||
// the .minimum component of the explicitly specified value | |||
int minContentHeight = 0; | |||
int maxCellHeight = 0; | |||
int effRowContentHeight = 0; | |||
int minRowBPD = 0; | |||
// The BPD of the biggest cell in the row | |||
int maxCellBPD = 0; | |||
for (int j = 0; j < row.getGridUnits().size(); j++) { | |||
assert maxColumnCount == 0 || maxColumnCount == row.getGridUnits().size(); | |||
maxColumnCount = Math.max(maxColumnCount, row.getGridUnits().size()); | |||
@@ -168,14 +168,12 @@ class RowGroupLayoutManager { | |||
tableRow = primary.getRow(); | |||
//Check for bpd on row, see CSS21, 17.5.3 Table height algorithms | |||
LengthRangeProperty bpd = tableRow.getBlockProgressionDimension(); | |||
if (!bpd.getMinimum(tableLM).isAuto()) { | |||
minContentHeight = Math.max( | |||
minContentHeight, | |||
bpd.getMinimum( | |||
tableLM).getLength().getValue(tableLM)); | |||
LengthRangeProperty rowBPD = tableRow.getBlockProgressionDimension(); | |||
if (!rowBPD.getMinimum(tableLM).isAuto()) { | |||
minRowBPD = Math.max(minRowBPD, | |||
rowBPD.getMinimum(tableLM).getLength().getValue(tableLM)); | |||
} | |||
MinOptMaxUtil.restrict(explicitRowHeights[rgi], bpd, tableLM); | |||
MinOptMaxUtil.restrict(explicitRowHeights[rgi], rowBPD, tableLM); | |||
} | |||
@@ -218,31 +216,27 @@ class RowGroupLayoutManager { | |||
} | |||
} | |||
//Calculate height of cell contents | |||
primary.setContentLength(ElementListUtils.calcContentLength( | |||
primary.getElements())); | |||
maxCellHeight = Math.max(maxCellHeight, primary.getContentLength()); | |||
//Calculate height of row, see CSS21, 17.5.3 Table height algorithms | |||
if (gu.isLastGridUnitRowSpan()) { | |||
int effCellContentHeight = minContentHeight; | |||
LengthRangeProperty bpd = primary.getCell().getBlockProgressionDimension(); | |||
if (!bpd.getMinimum(tableLM).isAuto()) { | |||
effCellContentHeight = Math.max( | |||
effCellContentHeight, | |||
bpd.getMinimum(tableLM).getLength().getValue(tableLM)); | |||
// The effective cell's bpd, after taking into account bpd | |||
// (possibly explicitly) set on the row or on the cell, and the | |||
// cell's content length | |||
int effectiveCellBPD = minRowBPD; | |||
LengthRangeProperty cellBPD = primary.getCell() | |||
.getBlockProgressionDimension(); | |||
if (!cellBPD.getMinimum(tableLM).isAuto()) { | |||
effectiveCellBPD = Math.max(effectiveCellBPD, | |||
cellBPD.getMinimum(tableLM).getLength().getValue(tableLM)); | |||
} | |||
if (!bpd.getOptimum(tableLM).isAuto()) { | |||
effCellContentHeight = Math.max( | |||
effCellContentHeight, | |||
bpd.getOptimum(tableLM).getLength().getValue(tableLM)); | |||
if (!cellBPD.getOptimum(tableLM).isAuto()) { | |||
effectiveCellBPD = Math.max(effectiveCellBPD, | |||
cellBPD.getOptimum(tableLM).getLength().getValue(tableLM)); | |||
} | |||
if (gu.getRowSpanIndex() == 0) { | |||
//TODO ATM only non-row-spanned cells are taken for this | |||
MinOptMaxUtil.restrict(explicitRowHeights[rgi], bpd, tableLM); | |||
MinOptMaxUtil.restrict(explicitRowHeights[rgi], cellBPD, tableLM); | |||
} | |||
effCellContentHeight = Math.max(effCellContentHeight, | |||
effectiveCellBPD = Math.max(effectiveCellBPD, | |||
primary.getContentLength()); | |||
int borderWidths; | |||
@@ -253,13 +247,12 @@ class RowGroupLayoutManager { | |||
borderWidths = primary.getHalfMaxBorderWidth(); | |||
} | |||
int padding = 0; | |||
effRowContentHeight = Math.max(effRowContentHeight, | |||
effCellContentHeight); | |||
maxCellBPD = Math.max(maxCellBPD, effectiveCellBPD); | |||
CommonBorderPaddingBackground cbpb | |||
= primary.getCell().getCommonBorderPaddingBackground(); | |||
padding += cbpb.getPaddingBefore(false, primary.getCellLM()); | |||
padding += cbpb.getPaddingAfter(false, primary.getCellLM()); | |||
int effRowHeight = effCellContentHeight | |||
int effRowHeight = effectiveCellBPD | |||
+ padding + borderWidths | |||
+ 2 * tableLM.getHalfBorderSeparationBPD(); | |||
for (int previous = 0; previous < gu.getRowSpanIndex(); previous++) { | |||
@@ -279,13 +272,13 @@ class RowGroupLayoutManager { | |||
row.setHeight(rowHeights[rgi]); | |||
row.setExplicitHeight(explicitRowHeights[rgi]); | |||
if (effRowContentHeight > row.getExplicitHeight().max) { | |||
if (maxCellBPD > row.getExplicitHeight().max) { | |||
log.warn(FONode.decorateWithContextInfo( | |||
"The contents of row " + (row.getIndex() + 1) | |||
+ " are taller than they should be (there is a" | |||
+ " block-progression-dimension or height constraint on the indicated row)." | |||
+ " Due to its contents the row grows" | |||
+ " to " + effRowContentHeight + " millipoints, but the row shouldn't get" | |||
+ " to " + maxCellBPD + " millipoints, but the row shouldn't get" | |||
+ " any taller than " + row.getExplicitHeight() + " millipoints.", | |||
row.getTableRow())); | |||
} |
@@ -42,10 +42,9 @@ class RowPainter { | |||
/** The fo:table-row containing the currently handled grid rows. */ | |||
private TableRow rowFO = null; | |||
private int colCount; | |||
private int yoffset = 0; | |||
private int accumulatedBPD = 0; | |||
private int currentRowOffset = 0; | |||
/** Currently handled row (= last encountered row). */ | |||
private EffRow lastRow = null; | |||
private EffRow currentRow = null; | |||
private LayoutContext layoutContext; | |||
/** | |||
* Index of the first row of the current part present on the current page. | |||
@@ -91,7 +90,7 @@ class RowPainter { | |||
} | |||
int getAccumulatedBPD() { | |||
return this.accumulatedBPD; | |||
return currentRowOffset; | |||
} | |||
/** | |||
@@ -101,16 +100,16 @@ class RowPainter { | |||
* @param tcpos a position representing the row fragment | |||
*/ | |||
void handleTableContentPosition(TableContentPosition tcpos) { | |||
if (lastRow != tcpos.row && lastRow != null) { | |||
if (tcpos.row != currentRow && currentRow != null) { | |||
addAreasAndFlushRow(false); | |||
} | |||
if (log.isDebugEnabled()) { | |||
log.debug("===handleTableContentPosition(" + tcpos); | |||
} | |||
rowFO = tcpos.row.getTableRow(); | |||
lastRow = tcpos.row; | |||
currentRow = tcpos.row; | |||
if (firstRowIndex < 0) { | |||
firstRowIndex = lastRow.getIndex(); | |||
firstRowIndex = currentRow.getIndex(); | |||
} | |||
Iterator partIter = tcpos.cellParts.iterator(); | |||
//Iterate over all grid units in the current step | |||
@@ -146,15 +145,15 @@ class RowPainter { | |||
* @param forcedFlush true if the elements must be drawn even if the row isn't | |||
* finished yet (last row on the page), or if the row is the last of the current table | |||
* part | |||
* @return the height of the (grid) row | |||
*/ | |||
int addAreasAndFlushRow(boolean forcedFlush) { | |||
void addAreasAndFlushRow(boolean forcedFlush) { | |||
int actualRowHeight = 0; | |||
if (log.isDebugEnabled()) { | |||
log.debug("Remembering yoffset for row " + lastRow.getIndex() + ": " + yoffset); | |||
log.debug("Remembering yoffset for row " + currentRow.getIndex() + ": " | |||
+ currentRowOffset); | |||
} | |||
recordRowOffset(lastRow.getIndex(), yoffset); | |||
recordRowOffset(currentRow.getIndex(), currentRowOffset); | |||
for (int i = 0; i < primaryGridUnits.length; i++) { | |||
if ((primaryGridUnits[i] != null) | |||
@@ -166,55 +165,38 @@ class RowPainter { | |||
actualRowHeight += 2 * tclm.getTableLM().getHalfBorderSeparationBPD(); | |||
//Add areas for row | |||
tclm.addRowBackgroundArea(rowFO, actualRowHeight, layoutContext.getRefIPD(), yoffset); | |||
tclm.addRowBackgroundArea(rowFO, actualRowHeight, layoutContext.getRefIPD(), | |||
currentRowOffset); | |||
for (int i = 0; i < primaryGridUnits.length; i++) { | |||
GridUnit currentGU = lastRow.getGridUnit(i); | |||
if (primaryGridUnits[i] != null) { | |||
if (forcedFlush || ((end[i] == primaryGridUnits[i].getElements().size() - 1) | |||
&& currentGU.isLastGridUnitRowSpan())) { | |||
//the last line in the "if" above is to avoid a premature end of a | |||
//row-spanned cell because no CellParts are generated after a cell is | |||
//finished with its content. | |||
//See table-cell_number-rows-spanned_bug38397.xml | |||
addAreasForCell(primaryGridUnits[i], start[i], end[i], lastRow, partBPD[i], | |||
actualRowHeight); | |||
primaryGridUnits[i] = null; | |||
start[i] = 0; | |||
end[i] = -1; | |||
partBPD[i] = 0; | |||
} | |||
} else if (!currentGU.isEmpty() | |||
&& currentGU.getColSpanIndex() == 0 | |||
GridUnit currentGU = currentRow.getGridUnit(i); | |||
if (!currentGU.isEmpty() && currentGU.getColSpanIndex() == 0 | |||
&& (forcedFlush || currentGU.isLastGridUnitRowSpan())) { | |||
//A row-spanned cell has finished contributing content on the previous page | |||
//and now still has to cause grid units to be painted. | |||
//See table-cell_page-break_span.xml | |||
addAreasForCell(currentGU.getPrimary(), start[i], end[i], lastRow, partBPD[i], | |||
addAreasForCell(currentGU.getPrimary(), start[i], end[i], currentRow, partBPD[i], | |||
actualRowHeight); | |||
primaryGridUnits[i] = null; | |||
start[i] = 0; | |||
end[i] = -1; | |||
partBPD[i] = 0; | |||
} | |||
} | |||
yoffset += actualRowHeight; | |||
accumulatedBPD += actualRowHeight; | |||
currentRowOffset += actualRowHeight; | |||
if (forcedFlush) { | |||
// Either the end of the page is reached, then this was the last call of this | |||
// method and we no longer care about lastRow; or the end of a table-part | |||
// method and we no longer care about currentRow; or the end of a table-part | |||
// (header, footer, body) has been reached, and the next row will anyway be | |||
// different from the current one, and this is unnecessary to recall this | |||
// method in the first lines of handleTableContentPosition, so we may reset | |||
// lastRow | |||
lastRow = null; | |||
// the following variables | |||
currentRow = null; | |||
firstRowIndex = -1; | |||
rowOffsets.clear(); | |||
} | |||
return actualRowHeight; | |||
} | |||
/** | |||
* Computes the total height of the part of the given cell spanning on the current | |||
* active row, including borders and paddings. The bpd is also stored in partBPD, and | |||
* it is ensured that the cell's or row's explicit height is respected. yoffset is | |||
* updated accordingly. | |||
* it is ensured that the cell's or row's explicit height is respected. | |||
* | |||
* @param pgu primary grid unit corresponding to the cell | |||
* @param start index of the first element of the cell occuring on the current page | |||
@@ -290,7 +272,7 @@ class RowPainter { | |||
len += pgu.getHalfMaxAfterBorderWidth(); | |||
} | |||
int cellOffset = getRowOffset(Math.max(pgu.getStartRow(), firstRowIndex)); | |||
len -= yoffset - cellOffset; | |||
len -= currentRowOffset - cellOffset; | |||
return len; | |||
} | |||
@@ -298,7 +280,7 @@ class RowPainter { | |||
EffRow row, int contentHeight, int rowHeight) { | |||
//Determine the first row in this sequence | |||
int startRowIndex = Math.max(pgu.getStartRow(), firstRowIndex); | |||
int lastRowIndex = lastRow.getIndex(); | |||
int currentRowIndex = currentRow.getIndex(); | |||
// In collapsing-border model, if the cell spans over several columns/rows then | |||
// dedicated areas will be created for each grid unit to hold the corresponding | |||
@@ -306,23 +288,24 @@ class RowPainter { | |||
// grid row spanned over by the cell | |||
int[] spannedGridRowHeights = null; | |||
if (!tclm.getTableLM().getTable().isSeparateBorderModel() && pgu.hasSpanning()) { | |||
spannedGridRowHeights = new int[lastRowIndex - startRowIndex + 1]; | |||
spannedGridRowHeights = new int[currentRowIndex - startRowIndex + 1]; | |||
int prevOffset = getRowOffset(startRowIndex); | |||
for (int i = 0; i < lastRowIndex - startRowIndex; i++) { | |||
for (int i = 0; i < currentRowIndex - startRowIndex; i++) { | |||
int newOffset = getRowOffset(startRowIndex + i + 1); | |||
spannedGridRowHeights[i] = newOffset - prevOffset; | |||
prevOffset = newOffset; | |||
} | |||
spannedGridRowHeights[lastRowIndex - startRowIndex] = rowHeight; | |||
spannedGridRowHeights[currentRowIndex - startRowIndex] = rowHeight; | |||
} | |||
int cellOffset = getRowOffset(startRowIndex); | |||
int effCellHeight = rowHeight; | |||
effCellHeight += yoffset - cellOffset; | |||
effCellHeight += currentRowOffset - cellOffset; | |||
if (log.isDebugEnabled()) { | |||
log.debug("Creating area for cell:"); | |||
log.debug(" current row: " + row.getIndex()); | |||
log.debug(" start row: " + pgu.getStartRow() + " " + yoffset + " " + cellOffset); | |||
log.debug(" start row: " + pgu.getStartRow() + " " + currentRowOffset + " " | |||
+ cellOffset); | |||
log.debug(" contentHeight: " + contentHeight + " rowHeight=" + rowHeight | |||
+ " effCellHeight=" + effCellHeight); | |||
} | |||
@@ -339,7 +322,7 @@ class RowPainter { | |||
} | |||
cellLM.addAreas(new KnuthPossPosIter(pgu.getElements(), startPos, endPos + 1), | |||
layoutContext, spannedGridRowHeights, startRowIndex - pgu.getStartRow(), | |||
lastRowIndex - pgu.getStartRow() + 1); | |||
currentRowIndex - pgu.getStartRow() + 1); | |||
} | |||
/** | |||
@@ -357,7 +340,7 @@ class RowPainter { | |||
* TableContentPosition will be created for this row. Thus its index will never be | |||
* recorded by the #handleTableContentPosition method. | |||
* | |||
* The yoffset for such a row is the same as the next non-empty row. It's needed | |||
* The offset of such a row is the same as the next non-empty row. It's needed | |||
* to correctly offset blocks for cells starting on this row. Hence the loop | |||
* below. | |||
*/ | |||
@@ -375,9 +358,4 @@ class RowPainter { | |||
private int getRowOffset(int rowIndex) { | |||
return ((Integer) rowOffsets.get(rowIndex - firstRowIndex)).intValue(); | |||
} | |||
void endPart() { | |||
firstRowIndex = -1; | |||
rowOffsets.clear(); | |||
} | |||
} |
@@ -418,7 +418,6 @@ public class TableContentLayoutManager implements PercentBaseContext { | |||
handleMarkersAndPositions(lst, body, firstPos, false, painter); | |||
} | |||
painter.addAreasAndFlushRow(true); | |||
painter.endPart(); | |||
} | |||
private void handleMarkersAndPositions(List positions, TableBody body, boolean firstPos, |
@@ -21,8 +21,10 @@ package org.apache.fop.pdf; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.io.Writer; | |||
import org.apache.commons.io.output.CountingOutputStream; | |||
import org.apache.fop.util.CloseBlockerOutputStream; | |||
/** | |||
@@ -169,8 +171,12 @@ public abstract class AbstractPDFStream extends PDFDictionary { | |||
* {@inheritDoc} | |||
*/ | |||
protected int output(OutputStream stream) throws IOException { | |||
int length = 0; | |||
setupFilterList(); | |||
CountingOutputStream cout = new CountingOutputStream(stream); | |||
Writer writer = PDFDocument.getWriterFor(cout); | |||
writer.write(getObjectID()); | |||
//int length = 0; | |||
StreamCache encodedStream = null; | |||
PDFNumber refLength = null; | |||
@@ -184,38 +190,21 @@ public abstract class AbstractPDFStream extends PDFDictionary { | |||
lengthEntry = new Integer(encodedStream.getSize() + 1); | |||
} | |||
byte[] p = encode(buildStreamDict(lengthEntry)); | |||
stream.write(p); | |||
length += p.length; | |||
populateStreamDict(lengthEntry); | |||
writeDictionary(cout, writer); | |||
//Send encoded stream to target OutputStream | |||
writer.flush(); | |||
if (encodedStream == null) { | |||
int bytesWritten = encodeAndWriteStream(stream, refLength); | |||
length += bytesWritten; | |||
encodeAndWriteStream(cout, refLength); | |||
} else { | |||
length += outputStreamData(encodedStream, stream); | |||
outputStreamData(encodedStream, cout); | |||
encodedStream.clear(); //Encoded stream can now be discarded | |||
} | |||
p = encode("\nendobj\n"); | |||
stream.write(p); | |||
length += p.length; | |||
return length; | |||
} | |||
/** | |||
* Constructs the dictionary for the stream. Override this method if you | |||
* need additional entries. | |||
* @param lengthEntry value for the /Length entry | |||
* @return the newly constructed dictionary | |||
*/ | |||
protected String buildStreamDict(Object lengthEntry) { | |||
StringBuffer sb = new StringBuffer(); | |||
sb.append(getObjectID()); | |||
populateStreamDict(lengthEntry); | |||
writeDictionary(sb); | |||
return sb.toString(); | |||
writer.write("\nendobj\n"); | |||
writer.flush(); | |||
return cout.getCount(); | |||
} | |||
/** |
@@ -159,15 +159,6 @@ public class BitmapImage implements PDFImage { | |||
return null; | |||
} | |||
/** | |||
* Get the soft mask reference for this image. | |||
* | |||
* @return the soft mask reference if any | |||
*/ | |||
public String getSoftMask() { | |||
return maskRef.toInlinePDFString(); | |||
} | |||
/** {@inheritDoc} */ | |||
public PDFReference getSoftMaskReference() { | |||
return maskRef; |
@@ -19,9 +19,14 @@ | |||
package org.apache.fop.pdf; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.io.Writer; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import org.apache.commons.io.output.CountingOutputStream; | |||
/** | |||
* Class representing an array object. | |||
*/ | |||
@@ -33,20 +38,21 @@ public class PDFArray extends PDFObject { | |||
/** | |||
* Create a new, empty array object | |||
* @param parent the array's parent if any | |||
*/ | |||
public PDFArray() { | |||
public PDFArray(PDFObject parent) { | |||
/* generic creation of PDF object */ | |||
super(); | |||
super(parent); | |||
} | |||
/** | |||
* Create the array object | |||
* | |||
* Create an array object. | |||
* @param parent the array's parent if any | |||
* @param values the actual array wrapped by this object | |||
*/ | |||
public PDFArray(int[] values) { | |||
public PDFArray(PDFObject parent, int[] values) { | |||
/* generic creation of PDF object */ | |||
super(); | |||
super(parent); | |||
for (int i = 0, c = values.length; i < c; i++) { | |||
this.values.add(new Integer(values[i])); | |||
@@ -54,25 +60,25 @@ public class PDFArray extends PDFObject { | |||
} | |||
/** | |||
* Create the array object | |||
* | |||
* Create an array object. | |||
* @param parent the array's parent if any | |||
* @param values the actual values wrapped by this object | |||
*/ | |||
public PDFArray(Collection values) { | |||
public PDFArray(PDFObject parent, Collection values) { | |||
/* generic creation of PDF object */ | |||
super(); | |||
super(parent); | |||
this.values.addAll(values); | |||
} | |||
/** | |||
* Create the array object | |||
* | |||
* @param parent the array's parent if any | |||
* @param values the actual array wrapped by this object | |||
*/ | |||
public PDFArray(Object[] values) { | |||
public PDFArray(PDFObject parent, Object[] values) { | |||
/* generic creation of PDF object */ | |||
super(); | |||
super(parent); | |||
for (int i = 0, c = values.length; i < c; i++) { | |||
this.values.add(values[i]); | |||
@@ -114,6 +120,17 @@ public class PDFArray extends PDFObject { | |||
return this.values.get(index); | |||
} | |||
/** | |||
* Adds a new value to the array. | |||
* @param obj the value | |||
*/ | |||
public void add(PDFObject obj) { | |||
if (obj != null) { | |||
obj.setParent(this); | |||
} | |||
this.values.add(obj); | |||
} | |||
/** | |||
* Adds a new value to the array. | |||
* @param obj the value | |||
@@ -130,27 +147,30 @@ public class PDFArray extends PDFObject { | |||
this.values.add(new Double(value)); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public String toPDFString() { | |||
StringBuffer p = new StringBuffer(64); | |||
/** {@inheritDoc} */ | |||
protected int output(OutputStream stream) throws IOException { | |||
CountingOutputStream cout = new CountingOutputStream(stream); | |||
Writer writer = PDFDocument.getWriterFor(cout); | |||
if (hasObjectNumber()) { | |||
p.append(getObjectID()); | |||
writer.write(getObjectID()); | |||
} | |||
p.append("["); | |||
writer.write('['); | |||
for (int i = 0; i < values.size(); i++) { | |||
if (i > 0) { | |||
p.append(" "); | |||
writer.write(' '); | |||
} | |||
Object obj = this.values.get(i); | |||
formatObject(obj, p); | |||
formatObject(obj, cout, writer); | |||
} | |||
p.append("]"); | |||
writer.write(']'); | |||
if (hasObjectNumber()) { | |||
p.append("\nendobj\n"); | |||
writer.write("\nendobj\n"); | |||
} | |||
return p.toString(); | |||
writer.flush(); | |||
return cout.getCount(); | |||
} | |||
} |
@@ -19,6 +19,12 @@ | |||
package org.apache.fop.pdf; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.io.Writer; | |||
import org.apache.commons.io.output.CountingOutputStream; | |||
/** | |||
* class representing a named destination | |||
*/ | |||
@@ -40,30 +46,24 @@ public class PDFDestination extends PDFObject { | |||
* @param goToRef Object reference to the GoTo Action | |||
*/ | |||
public PDFDestination(String idRef, Object goToRef) { | |||
super(); | |||
this.goToReference = goToRef; | |||
this.idRef = idRef; | |||
} | |||
/** | |||
* Creates the key/value pair for this destination entry for the name tree. | |||
* @return the formatted key/value pair | |||
*/ | |||
public String toKeyValuePair() { | |||
StringBuffer sb = new StringBuffer(); | |||
sb.append("(").append(getIDRef()).append(") "); | |||
if (goToReference instanceof PDFWritable) { | |||
sb.append(((PDFWritable)goToReference).toInlinePDFString()); | |||
} else { | |||
sb.append(goToReference); | |||
} | |||
return sb.toString(); | |||
} | |||
/** {@inheritDoc} */ | |||
protected String toPDFString() { | |||
return toKeyValuePair(); | |||
protected int output(OutputStream stream) throws IOException { | |||
CountingOutputStream cout = new CountingOutputStream(stream); | |||
Writer writer = PDFDocument.getWriterFor(cout); | |||
formatObject(getIDRef(), cout, writer); | |||
writer.write(' '); | |||
formatObject(goToReference, cout, writer); | |||
writer.flush(); | |||
return cout.getCount(); | |||
} | |||
/** | |||
* Sets the GoToReference in the associated DestinationData object. | |||
* | |||
@@ -124,9 +124,7 @@ public class PDFDestination extends PDFObject { | |||
return false; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
/** {@inheritDoc} */ | |||
public int hashCode() { | |||
return getIDRef().hashCode(); | |||
} |
@@ -40,7 +40,7 @@ public class PDFDests extends PDFNameTreeNode { | |||
*/ | |||
public PDFDests(List destinationList) { | |||
this(); | |||
setNames(new PDFArray(destinationList)); | |||
setNames(new PDFArray(this, destinationList)); | |||
} | |||
} |
@@ -19,10 +19,15 @@ | |||
package org.apache.fop.pdf; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.io.Writer; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.apache.commons.io.output.CountingOutputStream; | |||
/** | |||
* Class representing a PDF dictionary object | |||
*/ | |||
@@ -40,13 +45,20 @@ public class PDFDictionary extends PDFObject { | |||
protected List order = new java.util.ArrayList(); | |||
/** | |||
* Create the dictionary object | |||
* Create a new dictionary object. | |||
*/ | |||
public PDFDictionary() { | |||
/* generic creation of PDF object */ | |||
super(); | |||
} | |||
/** | |||
* Create a new dictionary object. | |||
* @param parent the object's parent if any | |||
*/ | |||
public PDFDictionary(PDFObject parent) { | |||
super(parent); | |||
} | |||
/** | |||
* Puts a new name/value pair. | |||
* @param name the name | |||
@@ -80,37 +92,53 @@ public class PDFDictionary extends PDFObject { | |||
return this.entries.get(name); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public String toPDFString() { | |||
StringBuffer p = new StringBuffer(64); | |||
/** {@inheritDoc} */ | |||
protected int output(OutputStream stream) throws IOException { | |||
CountingOutputStream cout = new CountingOutputStream(stream); | |||
Writer writer = PDFDocument.getWriterFor(cout); | |||
if (hasObjectNumber()) { | |||
p.append(getObjectID()); | |||
writer.write(getObjectID()); | |||
} | |||
writeDictionary(p); | |||
writeDictionary(cout, writer); | |||
if (hasObjectNumber()) { | |||
p.append("endobj\n"); | |||
writer.write("\nendobj\n"); | |||
} | |||
return p.toString(); | |||
writer.flush(); | |||
return cout.getCount(); | |||
} | |||
/** | |||
* Writes the contents of the dictionary to a StringBuffer. | |||
* @param sb the target StringBuffer | |||
* @param out the OutputStream (for binary content) | |||
* @param writer the Writer (for text content, wraps the above OutputStream) | |||
* @throws IOException if an I/O error occurs | |||
*/ | |||
protected void writeDictionary(StringBuffer sb) { | |||
sb.append("<<"); | |||
protected void writeDictionary(OutputStream out, Writer writer) throws IOException { | |||
writer.write("<<"); | |||
boolean compact = (this.order.size() <= 2); | |||
Iterator iter = this.order.iterator(); | |||
while (iter.hasNext()) { | |||
String key = (String)iter.next(); | |||
sb.append("\n /"); | |||
sb.append(key); | |||
sb.append(" "); | |||
if (compact) { | |||
writer.write(' '); | |||
} else { | |||
writer.write("\n "); | |||
} | |||
writer.write('/'); | |||
writer.write(key); | |||
writer.write(' '); | |||
Object obj = this.entries.get(key); | |||
formatObject(obj, sb); | |||
formatObject(obj, out, writer); | |||
} | |||
if (compact) { | |||
writer.write(' '); | |||
} else { | |||
writer.write('\n'); | |||
} | |||
sb.append("\n>>\n"); | |||
writer.write(">>\n"); | |||
} | |||
} |
@@ -24,15 +24,16 @@ import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.io.UnsupportedEncodingException; | |||
import java.io.Writer; | |||
import java.security.MessageDigest; | |||
import java.security.NoSuchAlgorithmException; | |||
import java.text.DateFormat; | |||
import java.text.SimpleDateFormat; | |||
import java.util.Collections; | |||
import java.util.Date; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Iterator; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
@@ -316,6 +317,22 @@ public class PDFDocument { | |||
} | |||
} | |||
/** | |||
* Creates and returns a Writer object wrapping the given OutputStream. The Writer is | |||
* buffered to reduce the number of calls to the encoding converter so don't forget | |||
* to <code>flush()</code> the Writer after use or before writing directly to the | |||
* underlying OutputStream. | |||
* @param out the OutputStream to write to | |||
* @return the requested Writer | |||
*/ | |||
public static Writer getWriterFor(OutputStream out) { | |||
try { | |||
return new java.io.BufferedWriter(new java.io.OutputStreamWriter(out, ENCODING)); | |||
} catch (UnsupportedEncodingException uee) { | |||
throw new Error("JVM doesn't support " + ENCODING + " encoding!"); | |||
} | |||
} | |||
/** | |||
* set the producer of the document | |||
* |
@@ -20,20 +20,20 @@ | |||
package org.apache.fop.pdf; | |||
// Java | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.io.IOException; | |||
import java.security.InvalidKeyException; | |||
import java.security.MessageDigest; | |||
import java.security.NoSuchAlgorithmException; | |||
import java.security.InvalidKeyException; | |||
import java.util.Random; | |||
import javax.crypto.BadPaddingException; | |||
import javax.crypto.Cipher; | |||
import javax.crypto.spec.SecretKeySpec; | |||
import javax.crypto.CipherOutputStream; | |||
import javax.crypto.IllegalBlockSizeException; | |||
import javax.crypto.BadPaddingException; | |||
import javax.crypto.NoSuchPaddingException; | |||
import java.util.Random; | |||
import javax.crypto.spec.SecretKeySpec; | |||
/** | |||
* class representing a /Filter /Standard object. | |||
@@ -358,17 +358,20 @@ public class PDFEncryptionJCE extends PDFObject implements PDFEncryption { | |||
if (this.encryptionKey == null) { | |||
throw new IllegalStateException("PDF Encryption has not been initialized"); | |||
} | |||
log.debug("encrypting with for " + number + " " + generation); | |||
byte[] hash = calcHash(number, generation); | |||
return encryptWithHash(data, hash, hash.length); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
/** {@inheritDoc} */ | |||
public byte[] encrypt(byte[] data, PDFObject refObj) { | |||
return encryptData(data, refObj.getObjectNumber(), refObj.getGeneration()); | |||
PDFObject o = refObj; | |||
while (o != null && !o.hasObjectNumber()) { | |||
o = o.getParent(); | |||
} | |||
if (o == null) { | |||
throw new IllegalStateException("No object number could be obtained for a PDF object"); | |||
} | |||
return encryptData(data, o.getObjectNumber(), o.getGeneration()); | |||
} | |||
private byte[] calcHash(int number, int generation) { |
@@ -849,6 +849,17 @@ public class PDFFactory { | |||
return names; | |||
} | |||
/** | |||
* Make a names dictionary (the /PageLabels object). | |||
* @return the new PDFPageLabels object | |||
*/ | |||
public PDFPageLabels makePageLabels() { | |||
PDFPageLabels pageLabels = new PDFPageLabels(); | |||
getDocument().assignObjectNumber(pageLabels); | |||
getDocument().addTrailerObject(pageLabels); | |||
return pageLabels; | |||
} | |||
/** | |||
* Make a the head object of the name dictionary (the /Dests object). | |||
* | |||
@@ -862,7 +873,7 @@ public class PDFFactory { | |||
//true for a "deep" structure (one node per entry), true for a "flat" structure | |||
if (deep) { | |||
dests = new PDFDests(); | |||
PDFArray kids = new PDFArray(); | |||
PDFArray kids = new PDFArray(dests); | |||
Iterator iter = destinationList.iterator(); | |||
while (iter.hasNext()) { | |||
PDFDestination dest = (PDFDestination)iter.next(); | |||
@@ -870,8 +881,9 @@ public class PDFFactory { | |||
getDocument().registerObject(node); | |||
node.setLowerLimit(dest.getIDRef()); | |||
node.setUpperLimit(dest.getIDRef()); | |||
node.setNames(new PDFArray()); | |||
node.getNames().add(dest); | |||
node.setNames(new PDFArray(node)); | |||
PDFArray names = node.getNames(); | |||
names.add(dest); | |||
kids.add(node); | |||
} | |||
dests.setLowerLimit(((PDFNameTreeNode)kids.get(0)).getLowerLimit()); | |||
@@ -1514,7 +1526,7 @@ public class PDFFactory { | |||
* @return the PDF Array with the int values | |||
*/ | |||
public PDFArray makeArray(int[] values) { | |||
PDFArray array = new PDFArray(values); | |||
PDFArray array = new PDFArray(null, values); | |||
getDocument().registerObject(array); | |||
return array; |
@@ -282,7 +282,7 @@ public class PDFFilterList { | |||
} | |||
private void putFilterEntries(PDFDictionary dict, List names) { | |||
PDFArray array = new PDFArray(); | |||
PDFArray array = new PDFArray(dict); | |||
for (int i = 0, c = names.size(); i < c; i++) { | |||
final String name = (String)names.get(i); | |||
if (name.length() > 0) { | |||
@@ -328,7 +328,7 @@ public class PDFFilterList { | |||
private void putDecodeParams(PDFDictionary dict, List parms) { | |||
boolean needParmsEntry = false; | |||
PDFArray array = new PDFArray(); | |||
PDFArray array = new PDFArray(dict); | |||
for (int i = 0, c = parms.size(); i < c; i++) { | |||
Object obj = parms.get(i); | |||
if (obj != null) { |
@@ -63,7 +63,7 @@ public class PDFFormXObject extends PDFXObject { | |||
public void setBBox(Rectangle2D bbox) { | |||
PDFArray array = (PDFArray)get("BBox"); | |||
if (array == null) { | |||
array = new PDFArray(); | |||
array = new PDFArray(this); | |||
array.add(bbox.getX()); | |||
array.add(bbox.getY()); | |||
array.add(bbox.getWidth()); | |||
@@ -105,7 +105,7 @@ public class PDFFormXObject extends PDFXObject { | |||
double[] m = new double[6]; | |||
at.getMatrix(m); | |||
if (array == null) { | |||
array = new PDFArray(); | |||
array = new PDFArray(this); | |||
array.add(m[0]); | |||
array.add(m[1]); | |||
array.add(m[2]); | |||
@@ -170,7 +170,7 @@ public class PDFFormXObject extends PDFXObject { | |||
/** {@inheritDoc} */ | |||
protected void populateStreamDict(Object lengthEntry) { | |||
if (get("Matrix") == null) { | |||
put("Matrix", new PDFArray(new int[] {1, 0, 0, 1, 0, 0})); | |||
put("Matrix", new PDFArray(this, new int[] {1, 0, 0, 1, 0, 0})); | |||
} | |||
put("Resources", resRef); | |||
super.populateStreamDict(lengthEntry); |
@@ -105,11 +105,8 @@ public interface PDFImage { | |||
/** | |||
* Get the PDF reference for a soft mask. | |||
* | |||
* @return the PDF reference for a soft mask image | |||
* @return the PDF reference for a soft mask image (or null if there's no soft mask) | |||
*/ | |||
String getSoftMask(); | |||
PDFReference getSoftMaskReference(); | |||
/** @return true for CMYK images generated by Adobe Photoshop */ |
@@ -94,7 +94,8 @@ public class PDFImageXObject extends PDFXObject { | |||
PDFICCStream pdfICCStream = pdfimage.getICCStream(); | |||
if (pdfICCStream != null) { | |||
put("ColorSpace", new PDFArray(new Object[] {new PDFName("ICCBased"), pdfICCStream})); | |||
put("ColorSpace", new PDFArray(this, | |||
new Object[] {new PDFName("ICCBased"), pdfICCStream})); | |||
} else { | |||
PDFDeviceColorSpace cs = pdfimage.getColorSpace(); | |||
put("ColorSpace", new PDFName(cs.getName())); | |||
@@ -107,7 +108,7 @@ public class PDFImageXObject extends PDFXObject { | |||
*/ | |||
final Float zero = new Float(0.0f); | |||
final Float one = new Float(1.0f); | |||
PDFArray decode = new PDFArray(); | |||
PDFArray decode = new PDFArray(this); | |||
for (int i = 0, c = pdfimage.getColorSpace().getNumComponents(); i < c; i++) { | |||
decode.add(one); | |||
decode.add(zero); | |||
@@ -117,7 +118,7 @@ public class PDFImageXObject extends PDFXObject { | |||
if (pdfimage.isTransparent()) { | |||
PDFColor transp = pdfimage.getTransparentColor(); | |||
PDFArray mask = new PDFArray(); | |||
PDFArray mask = new PDFArray(this); | |||
if (pdfimage.getColorSpace().isGrayColorSpace()) { | |||
mask.add(new Integer(transp.red255())); | |||
mask.add(new Integer(transp.red255())); |
@@ -19,6 +19,10 @@ | |||
package org.apache.fop.pdf; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.io.Writer; | |||
/** | |||
* Class representing a PDF name object. | |||
*/ | |||
@@ -65,15 +69,14 @@ public class PDFName implements PDFWritable { | |||
sb.append(DIGITS[ch & 0x0F]); | |||
} | |||
/** {@inheritDoc} */ | |||
public String toInlinePDFString() { | |||
public String toString() { | |||
return this.name; | |||
} | |||
/** {@inheritDoc} */ | |||
public String toString() { | |||
return toInlinePDFString(); | |||
public void outputInline(OutputStream out, Writer writer) throws IOException { | |||
writer.write(toString()); | |||
} | |||
} |
@@ -108,7 +108,7 @@ public class PDFNameTreeNode extends PDFDictionary { | |||
private PDFArray prepareLimitsArray() { | |||
PDFArray limits = (PDFArray)get(LIMITS); | |||
if (limits == null) { | |||
limits = new PDFArray(new Object[2]); | |||
limits = new PDFArray(this, new Object[2]); | |||
put(LIMITS, limits); | |||
} | |||
if (limits.length() != 2) { |
@@ -19,6 +19,10 @@ | |||
package org.apache.fop.pdf; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.io.Writer; | |||
/** | |||
* Class representing a PDF name object. | |||
*/ | |||
@@ -35,13 +39,13 @@ public final class PDFNull implements PDFWritable { | |||
} | |||
/** {@inheritDoc} */ | |||
public String toInlinePDFString() { | |||
public String toString() { | |||
return "null"; | |||
} | |||
/** {@inheritDoc} */ | |||
public String toString() { | |||
return toInlinePDFString(); | |||
public void outputInline(OutputStream out, Writer writer) throws IOException { | |||
writer.write(toString()); | |||
} | |||
} |
@@ -0,0 +1,121 @@ | |||
/* | |||
* 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.pdf; | |||
/** | |||
* Class representing a PDF number tree node. | |||
*/ | |||
public class PDFNumberTreeNode extends PDFDictionary { | |||
private static final String KIDS = "Kids"; | |||
private static final String NUMS = "Nums"; | |||
private static final String LIMITS = "Limits"; | |||
/** | |||
* create a named destination | |||
*/ | |||
public PDFNumberTreeNode() { | |||
/* generic creation of PDF object */ | |||
super(); | |||
} | |||
/** | |||
* Sets the Kids array. | |||
* @param kids the Kids array | |||
*/ | |||
public void setKids(PDFArray kids) { | |||
put(KIDS, kids); | |||
} | |||
/** | |||
* Returns the Kids array. | |||
* @return the Kids array | |||
*/ | |||
public PDFArray getKids() { | |||
return (PDFArray)get(KIDS); | |||
} | |||
/** | |||
* Sets the Nums array. | |||
* @param nums the Nums array | |||
*/ | |||
public void setNums(PDFNumsArray nums) { | |||
put(NUMS, nums); | |||
} | |||
/** | |||
* Returns the Nums array. | |||
* @return the Nums array | |||
*/ | |||
public PDFNumsArray getNums() { | |||
return (PDFNumsArray)get(NUMS); | |||
} | |||
/** | |||
* Sets the lower limit value of the Limits array. | |||
* @param key the lower limit value | |||
*/ | |||
public void setLowerLimit(Integer key) { | |||
PDFArray limits = prepareLimitsArray(); | |||
limits.set(0, key); | |||
} | |||
/** | |||
* Returns the lower limit value of the Limits array. | |||
* @return the lower limit value | |||
*/ | |||
public Integer getLowerLimit() { | |||
PDFArray limits = prepareLimitsArray(); | |||
return (Integer)limits.get(0); | |||
} | |||
/** | |||
* Sets the upper limit value of the Limits array. | |||
* @param key the upper limit value | |||
*/ | |||
public void setUpperLimit(Integer key) { | |||
PDFArray limits = prepareLimitsArray(); | |||
limits.set(1, key); | |||
} | |||
/** | |||
* Returns the upper limit value of the Limits array. | |||
* @return the upper limit value | |||
*/ | |||
public Integer getUpperLimit() { | |||
PDFArray limits = prepareLimitsArray(); | |||
return (Integer)limits.get(1); | |||
} | |||
private PDFArray prepareLimitsArray() { | |||
PDFArray limits = (PDFArray)get(LIMITS); | |||
if (limits == null) { | |||
limits = new PDFArray(this, new Object[2]); | |||
put(LIMITS, limits); | |||
} | |||
if (limits.length() != 2) { | |||
throw new IllegalStateException("Limits array must have 2 entries"); | |||
} | |||
return limits; | |||
} | |||
} | |||
@@ -0,0 +1,104 @@ | |||
/* | |||
* 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.pdf; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.io.Writer; | |||
import java.util.Iterator; | |||
import java.util.Map; | |||
import java.util.SortedMap; | |||
import org.apache.commons.io.output.CountingOutputStream; | |||
/** | |||
* Class representing an "Nums" array object (for Number Trees). | |||
*/ | |||
public class PDFNumsArray extends PDFObject { | |||
/** Sorted Map holding the values of this array. */ | |||
protected SortedMap map = new java.util.TreeMap(); | |||
/** | |||
* Create a new, empty array object. | |||
* @param parent the object's parent if any | |||
*/ | |||
public PDFNumsArray(PDFObject parent) { | |||
super(parent); | |||
} | |||
/** | |||
* Returns the length of the array | |||
* @return the length of the array | |||
*/ | |||
public int length() { | |||
return this.map.size(); | |||
} | |||
/** | |||
* Sets an entry. | |||
* @param key the key of the value to set | |||
* @param obj the new value | |||
*/ | |||
public void put(int key, Object obj) { | |||
this.map.put(new Integer(key), obj); | |||
} | |||
/** | |||
* Gets an entry. | |||
* @param key the key of requested value | |||
* @return the requested value | |||
*/ | |||
public Object get(int key) { | |||
return this.map.get(new Integer(key)); | |||
} | |||
/** {@inheritDoc} */ | |||
protected int output(OutputStream stream) throws IOException { | |||
CountingOutputStream cout = new CountingOutputStream(stream); | |||
Writer writer = PDFDocument.getWriterFor(cout); | |||
if (hasObjectNumber()) { | |||
writer.write(getObjectID()); | |||
} | |||
writer.write('['); | |||
boolean first = true; | |||
Iterator iter = this.map.entrySet().iterator(); | |||
while (iter.hasNext()) { | |||
Map.Entry entry = (Map.Entry)iter.next(); | |||
if (!first) { | |||
writer.write(" "); | |||
} | |||
first = false; | |||
formatObject(entry.getKey(), cout, writer); | |||
writer.write(" "); | |||
formatObject(entry.getValue(), cout, writer); | |||
} | |||
writer.write(']'); | |||
if (hasObjectNumber()) { | |||
writer.write("\nendobj\n"); | |||
} | |||
writer.flush(); | |||
return cout.getCount(); | |||
} | |||
} |
@@ -22,6 +22,7 @@ package org.apache.fop.pdf; | |||
// Java | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.io.Writer; | |||
import java.text.SimpleDateFormat; | |||
import java.util.Calendar; | |||
import java.util.Date; | |||
@@ -57,6 +58,9 @@ public abstract class PDFObject implements PDFWritable { | |||
*/ | |||
private PDFDocument document; | |||
/** the parent PDFObject (may be null and may not always be set, needed for encryption) */ | |||
private PDFObject parent; | |||
/** | |||
* Returns the object's number. | |||
* @return the PDF Object number | |||
@@ -68,6 +72,21 @@ public abstract class PDFObject implements PDFWritable { | |||
return this.objnum; | |||
} | |||
/** | |||
* Default constructor. | |||
*/ | |||
public PDFObject() { | |||
//nop | |||
} | |||
/** | |||
* Constructor for direct objects. | |||
* @param parent the containing PDFObject instance | |||
*/ | |||
public PDFObject(PDFObject parent) { | |||
setParent(parent); | |||
} | |||
/** | |||
* Indicates whether this PDFObject has already been assigned an | |||
* object number. | |||
@@ -102,7 +121,13 @@ public abstract class PDFObject implements PDFWritable { | |||
* has not been assigned) | |||
*/ | |||
public final PDFDocument getDocument() { | |||
return this.document; | |||
if (this.document != null) { | |||
return this.document; | |||
} else if (getParent() != null) { | |||
return getParent().getDocument(); | |||
} else { | |||
return null; | |||
} | |||
} | |||
/** | |||
@@ -127,6 +152,22 @@ public abstract class PDFObject implements PDFWritable { | |||
this.document = doc; | |||
} | |||
/** | |||
* Returns this objects's parent. The parent is null if it is a "direct object". | |||
* @return the parent or null if there's no parent (or it hasn't been set) | |||
*/ | |||
public PDFObject getParent() { | |||
return this.parent; | |||
} | |||
/** | |||
* Sets the direct parent object. | |||
* @param parent the direct parent | |||
*/ | |||
public void setParent(PDFObject parent) { | |||
this.parent = parent; | |||
} | |||
/** | |||
* Returns the PDF representation of the Object ID. | |||
* @return the Object ID | |||
@@ -169,6 +210,16 @@ public abstract class PDFObject implements PDFWritable { | |||
return pdf.length; | |||
} | |||
/** {@inheritDoc} */ | |||
public void outputInline(OutputStream out, Writer writer) throws IOException { | |||
if (hasObjectNumber()) { | |||
writer.write(referencePDF()); | |||
} else { | |||
writer.flush(); | |||
output(out); | |||
} | |||
} | |||
/** | |||
* Encodes the object as a byte array for output to a PDF file. | |||
* | |||
@@ -184,7 +235,9 @@ public abstract class PDFObject implements PDFWritable { | |||
* is normally converted/encoded to a byte array by toPDF(). Only use | |||
* this method to implement the serialization if the object can be fully | |||
* represented as text. If the PDF representation of the object contains | |||
* binary content use toPDF() or output(OutputStream) instead. | |||
* binary content use toPDF() or output(OutputStream) instead. This applies | |||
* to any object potentially containing a string object because string object | |||
* are encrypted and therefore need to be binary. | |||
* @return String the String representation | |||
*/ | |||
protected String toPDFString() { | |||
@@ -192,20 +245,6 @@ public abstract class PDFObject implements PDFWritable { | |||
+ "Use output(OutputStream) instead."); | |||
} | |||
/** | |||
* Returns a representation of this object for in-object placement, i.e. if the object | |||
* has an object number its reference is returned. Otherwise, its PDF representation is | |||
* returned. | |||
* @return the String representation | |||
*/ | |||
public String toInlinePDFString() { | |||
if (hasObjectNumber()) { | |||
return referencePDF(); | |||
} else { | |||
return toPDFString(); | |||
} | |||
} | |||
/** | |||
* Converts text to a byte array for writing to a PDF file. | |||
* @param text text to convert/encode | |||
@@ -250,23 +289,27 @@ public abstract class PDFObject implements PDFWritable { | |||
/** | |||
* Formats an object for serialization to PDF. | |||
* @param obj the object | |||
* @param sb the StringBuffer to write to | |||
* @param out the OutputStream to write to | |||
* @param writer a Writer for text content (will always be a wrapper around the above | |||
* OutputStream. Make sure <code>flush</code> is called when mixing calls) | |||
* @throws IOException If an I/O error occurs | |||
*/ | |||
protected void formatObject(Object obj, StringBuffer sb) { | |||
protected void formatObject(Object obj, OutputStream out, Writer writer) throws IOException { | |||
if (obj == null) { | |||
sb.append("null"); | |||
writer.write("null"); | |||
} else if (obj instanceof PDFWritable) { | |||
sb.append(((PDFWritable)obj).toInlinePDFString()); | |||
((PDFWritable)obj).outputInline(out, writer); | |||
} else if (obj instanceof Number) { | |||
if (obj instanceof Double || obj instanceof Float) { | |||
sb.append(PDFNumber.doubleOut(((Number)obj).doubleValue())); | |||
writer.write(PDFNumber.doubleOut(((Number)obj).doubleValue())); | |||
} else { | |||
sb.append(obj); | |||
writer.write(obj.toString()); | |||
} | |||
} else if (obj instanceof Boolean) { | |||
sb.append(obj); | |||
writer.write(obj.toString()); | |||
} else { | |||
sb.append("(").append(obj).append(")"); | |||
writer.flush(); | |||
out.write(encodeText(obj.toString())); | |||
} | |||
} | |||
@@ -0,0 +1,48 @@ | |||
/* | |||
* 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.pdf; | |||
/** | |||
* Class representing a PDF /PageLabels dictionary. | |||
*/ | |||
public class PDFPageLabels extends PDFNumberTreeNode { | |||
/** | |||
* Create the /PageLabels dictionary | |||
*/ | |||
public PDFPageLabels() { | |||
super(); | |||
} | |||
/** | |||
* Returns the Nums object | |||
* @return the Nums object (an empty PDFNumsArray for the "/Nums" entry is created | |||
* if it doesn't exist) | |||
*/ | |||
public PDFNumsArray getNums() { | |||
PDFNumsArray nums = super.getNums(); | |||
if (nums == null) { | |||
nums = new PDFNumsArray(this); | |||
setNums(nums); | |||
} | |||
return nums; | |||
} | |||
} |
@@ -19,6 +19,9 @@ | |||
package org.apache.fop.pdf; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.io.Writer; | |||
import java.lang.ref.Reference; | |||
import java.lang.ref.SoftReference; | |||
@@ -68,13 +71,13 @@ public class PDFReference implements PDFWritable { | |||
} | |||
/** {@inheritDoc} */ | |||
public String toInlinePDFString() { | |||
public String toString() { | |||
return this.indirectReference; | |||
} | |||
/** {@inheritDoc} */ | |||
public String toString() { | |||
return toInlinePDFString(); | |||
public void outputInline(OutputStream out, Writer writer) throws IOException { | |||
writer.write(toString()); | |||
} | |||
} |
@@ -19,12 +19,10 @@ | |||
package org.apache.fop.pdf; | |||
import java.util.List; | |||
/** | |||
* class representing a Root (/Catalog) object | |||
* Class representing a Root (/Catalog) object. | |||
*/ | |||
public class PDFRoot extends PDFObject { | |||
public class PDFRoot extends PDFDictionary { | |||
/** | |||
* Use no page mode setting, default | |||
@@ -46,27 +44,13 @@ public class PDFRoot extends PDFObject { | |||
*/ | |||
public static final int PAGEMODE_FULLSCREEN = 3; | |||
/** | |||
* the /Pages object that is root of the Pages hierarchy | |||
*/ | |||
protected PDFPages rootPages; | |||
/** | |||
* Root outline object | |||
*/ | |||
private PDFOutline outline; | |||
/** Optional Metadata object */ | |||
private PDFMetadata metadata; | |||
private static final PDFName[] PAGEMODE_NAMES = new PDFName[] { | |||
new PDFName("UseNone"), | |||
new PDFName("UseOutlines"), | |||
new PDFName("UseThumbs"), | |||
new PDFName("FullScreen"), | |||
}; | |||
/** The array of OutputIntents */ | |||
private List outputIntents; | |||
/** the /Dests object, if this PDF has a Names Dictionary */ | |||
private PDFNames names; | |||
private int pageMode = PAGEMODE_USENONE; | |||
/** | |||
* create a Root (/Catalog) object. NOTE: The PDFRoot | |||
* object must be created before the PDF document is | |||
@@ -80,25 +64,45 @@ public class PDFRoot extends PDFObject { | |||
public PDFRoot(int objnum, PDFPages pages) { | |||
super(); | |||
setObjectNumber(objnum); | |||
put("Type", new PDFName("Catalog")); | |||
setRootPages(pages); | |||
} | |||
/** | |||
* Set the page mode for the PDF document. | |||
* | |||
* @param mode the page mode | |||
* @param mode the page mode (one of PAGEMODE_*) | |||
*/ | |||
public void setPageMode(int mode) { | |||
pageMode = mode; | |||
put("PageMode", PAGEMODE_NAMES[mode]); | |||
} | |||
/** | |||
* Returns the currently active /PageMode. | |||
* @return the /PageMode (one of PAGEMODE_*) | |||
*/ | |||
public int getPageMode() { | |||
PDFName mode = (PDFName)get("PageMode"); | |||
if (mode != null) { | |||
for (int i = 0; i < PAGEMODE_NAMES.length; i++) { | |||
if (PAGEMODE_NAMES[i].equals(mode)) { | |||
return i; | |||
} | |||
} | |||
throw new IllegalStateException("Unknown /PageMode encountered: " + mode); | |||
} else { | |||
return PAGEMODE_USENONE; | |||
} | |||
} | |||
/** | |||
* add a /Page object to the root /Pages object | |||
* | |||
* @param page the /Page object to add | |||
*/ | |||
public void addPage(PDFPage page) { | |||
this.rootPages.addPage(page); | |||
PDFPages pages = getRootPages(); | |||
pages.addPage(page); | |||
} | |||
/** | |||
@@ -107,16 +111,50 @@ public class PDFRoot extends PDFObject { | |||
* @param pages the /Pages object to set as root | |||
*/ | |||
public void setRootPages(PDFPages pages) { | |||
this.rootPages = pages; | |||
put("Pages", pages.makeReference()); | |||
} | |||
/** | |||
* Returns the /PageLabels object. | |||
* @return the /PageLabels object if set, null otherwise. | |||
* @since PDF 1.3 | |||
*/ | |||
public PDFPages getRootPages() { | |||
PDFReference ref = (PDFReference)get("Pages"); | |||
return (ref != null ? (PDFPages)ref.getObject() : null); | |||
} | |||
/** | |||
* Sets the /PageLabels object. | |||
* @param pageLabels the /PageLabels object | |||
*/ | |||
public void setPageLabels(PDFPageLabels pageLabels) { | |||
put("PageLabels", pageLabels.makeReference()); | |||
} | |||
/** | |||
* Returns the /PageLabels object. | |||
* @return the /PageLabels object if set, null otherwise. | |||
* @since PDF 1.3 | |||
*/ | |||
public PDFPageLabels getPageLabels() { | |||
PDFReference ref = (PDFReference)get("PageLabels"); | |||
return (ref != null ? (PDFPageLabels)ref.getObject() : null); | |||
} | |||
/** | |||
* Set the root outline for the PDF document. | |||
* | |||
* @param out the root PDF Outline | |||
*/ | |||
public void setRootOutline(PDFOutline out) { | |||
outline = out; | |||
put("Outlines", out.makeReference()); | |||
//Set /PageMode to /UseOutlines by default if no other mode has been set | |||
PDFName mode = (PDFName)get("PageMode"); | |||
if (mode == null) { | |||
setPageMode(PAGEMODE_USEOUTLINES); | |||
} | |||
} | |||
/** | |||
@@ -125,24 +163,27 @@ public class PDFRoot extends PDFObject { | |||
* @return the root PDF Outline | |||
*/ | |||
public PDFOutline getRootOutline() { | |||
return outline; | |||
PDFReference ref = (PDFReference)get("Outlines"); | |||
return (ref != null ? (PDFOutline)ref.getObject() : null); | |||
} | |||
/** | |||
* Set the Names object. | |||
* Set the /Names object. | |||
* @param names the Names object | |||
* @since PDF 1.2 | |||
*/ | |||
public void setNames(PDFNames names) { | |||
this.names = names; | |||
put("Names", names.makeReference()); | |||
} | |||
/** | |||
* Returns the /Names object. | |||
* @return the Names object if set, null otherwise. | |||
* @since PDF 1.2 | |||
*/ | |||
public PDFNames getNames() { | |||
return this.names; | |||
PDFReference ref = (PDFReference)get("Names"); | |||
return (ref != null ? (PDFNames)ref.getObject() : null); | |||
} | |||
/** | |||
@@ -151,78 +192,44 @@ public class PDFRoot extends PDFObject { | |||
* @since PDF 1.4 | |||
*/ | |||
public void setMetadata(PDFMetadata meta) { | |||
this.metadata = meta; | |||
if (getDocumentSafely().getPDFVersion() >= PDFDocument.PDF_VERSION_1_4) { | |||
put("Metadata", meta.makeReference()); | |||
} | |||
} | |||
/** | |||
* @return the Metadata object if set, null otherwise. | |||
* Returns the /Metadata object | |||
* @return the /Metadata object if set, null otherwise. | |||
* @since PDF 1.4 | |||
*/ | |||
public PDFMetadata getMetadata() { | |||
return this.metadata; | |||
PDFReference ref = (PDFReference)get("Metadata"); | |||
return (ref != null ? (PDFMetadata)ref.getObject() : null); | |||
} | |||
/** | |||
* Adds an OutputIntent to the PDF | |||
* @param outputIntent the OutputIntent dictionary | |||
* Returns the /OutputIntents array. | |||
* @return the /OutputIntents array or null if it doesn't exist | |||
* @since PDF 1.4 | |||
*/ | |||
public void addOutputIntent(PDFOutputIntent outputIntent) { | |||
if (this.outputIntents == null) { | |||
this.outputIntents = new java.util.ArrayList(); | |||
} | |||
this.outputIntents.add(outputIntent); | |||
public PDFArray getOutputIntents() { | |||
return (PDFArray)get("OutputIntents"); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public String toPDFString() { | |||
StringBuffer p = new StringBuffer(128); | |||
p.append(getObjectID()); | |||
p.append("<< /Type /Catalog\n /Pages " | |||
+ this.rootPages.referencePDF() | |||
+ "\n"); | |||
if (outline != null) { | |||
p.append(" /Outlines " + outline.referencePDF() + "\n"); | |||
p.append(" /PageMode /UseOutlines\n"); | |||
} else { | |||
switch (pageMode) { | |||
case PAGEMODE_USEOUTLINES: | |||
p.append(" /PageMode /UseOutlines\n"); | |||
break; | |||
case PAGEMODE_USETHUMBS: | |||
p.append(" /PageMode /UseThumbs\n"); | |||
break; | |||
case PAGEMODE_FULLSCREEN: | |||
p.append(" /PageMode /FullScreen\n"); | |||
break; | |||
case PAGEMODE_USENONE: | |||
default: | |||
break; | |||
} | |||
} | |||
if (getDocumentSafely().hasDestinations() && getNames() != null) { | |||
p.append(" /Names " + getNames().referencePDF() + "\n"); | |||
} | |||
if (getMetadata() != null | |||
&& getDocumentSafely().getPDFVersion() >= PDFDocument.PDF_VERSION_1_4) { | |||
p.append(" /Metadata " + getMetadata().referencePDF() + "\n"); | |||
} | |||
if (this.outputIntents != null | |||
&& this.outputIntents.size() > 0 | |||
&& getDocumentSafely().getPDFVersion() >= PDFDocument.PDF_VERSION_1_4) { | |||
p.append(" /OutputIntents ["); | |||
for (int i = 0, c = this.outputIntents.size(); i < c; i++) { | |||
PDFOutputIntent outputIntent = (PDFOutputIntent)this.outputIntents.get(i); | |||
if (i > 0) { | |||
p.append(" "); | |||
} | |||
p.append(outputIntent.referencePDF()); | |||
* Adds an OutputIntent to the PDF | |||
* @param outputIntent the OutputIntent dictionary | |||
* @since PDF 1.4 | |||
*/ | |||
public void addOutputIntent(PDFOutputIntent outputIntent) { | |||
if (getDocumentSafely().getPDFVersion() >= PDFDocument.PDF_VERSION_1_4) { | |||
PDFArray outputIntents = getOutputIntents(); | |||
if (outputIntents == null) { | |||
outputIntents = new PDFArray(this); | |||
put("OutputIntents", outputIntents); | |||
} | |||
p.append("]\n"); | |||
outputIntents.add(outputIntent); | |||
} | |||
p.append(">>\nendobj\n"); | |||
return p.toString(); | |||
} | |||
} |
@@ -19,6 +19,10 @@ | |||
package org.apache.fop.pdf; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.io.Writer; | |||
/** | |||
* This interface is implemented by classes that can be serialized to a PDF file either by | |||
* serializing the object or by writing a indirect reference to the actual object. | |||
@@ -26,11 +30,13 @@ package org.apache.fop.pdf; | |||
public interface PDFWritable { | |||
/** | |||
* Returns a representation of this object for in-object placement, i.e. if the object | |||
* has an object number its reference is returned. Otherwise, its PDF representation is | |||
* returned. | |||
* @return the String representation | |||
* Writes a "direct object" (inline object) representation to the stream. A Writer is given | |||
* for optimized encoding of text content. Since the Writer is buffered, make sure | |||
* <code>flush()</code> is called before any direct calls to <code>out</code> are made. | |||
* @param out the OutputStream (for binary content) | |||
* @param writer the Writer (for text content, wraps the above OutputStream) | |||
* @throws IOException if an I/O error occurs | |||
*/ | |||
String toInlinePDFString(); | |||
void outputInline(OutputStream out, Writer writer) throws IOException; | |||
} |
@@ -118,7 +118,7 @@ public class PrintRendererConfigurator extends AbstractRendererConfigurator | |||
boolean strict, FontCache fontCache) throws FOPException { | |||
List fontInfoList = new java.util.ArrayList(); | |||
Configuration fonts = cfg.getChild("fonts"); | |||
Configuration fonts = cfg.getChild("fonts", false); | |||
if (fonts != null) { | |||
long start = 0; | |||
if (log.isDebugEnabled()) { |
@@ -21,12 +21,14 @@ package org.apache.fop.render.afp.extensions; | |||
import java.io.Serializable; | |||
import org.apache.fop.fo.extensions.ExtensionAttachment; | |||
import org.apache.fop.util.XMLizable; | |||
import org.xml.sax.ContentHandler; | |||
import org.xml.sax.SAXException; | |||
import org.xml.sax.helpers.AttributesImpl; | |||
import org.apache.xmlgraphics.util.XMLizable; | |||
import org.apache.fop.fo.extensions.ExtensionAttachment; | |||
/** | |||
* This is the pass-through value object for the PostScript extension. | |||
*/ |
@@ -1,79 +0,0 @@ | |||
/* | |||
* 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.modca; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
/** | |||
* The End Named Page Group (ENG) structured field terminates a page group that was | |||
* initiated by a Begin Named Page Group structured field. | |||
* | |||
* Note :This object will be used to represent an ENG | |||
* structured field. It is necessary as you can't end | |||
* a PageGroup because you don't know where the group | |||
* will end (as this is controlled by the tags in the FO). | |||
* <p> | |||
* | |||
*/ | |||
public class EndPageGroup extends AbstractNamedAFPObject { | |||
/** | |||
* Main constructor | |||
* @param groupId the group id | |||
*/ | |||
public EndPageGroup(String groupId) { | |||
super(groupId); | |||
if (log.isDebugEnabled()) { | |||
log.debug("A ENG is being created for group: " + groupId); | |||
} | |||
} | |||
/** | |||
* Accessor method to write the AFP datastream for the End Page Group. | |||
* @param os The stream to write to | |||
* @throws java.io.IOException thrown if an I/O exception of some sort has occurred | |||
*/ | |||
public void writeDataStream(OutputStream os) | |||
throws IOException { | |||
byte[] data = new byte[17]; | |||
data[0] = 0x5A; // Structured field identifier | |||
data[1] = 0x00; // Length byte 1 | |||
data[2] = 0x10; // Length byte 2 | |||
data[3] = (byte) 0xD3; // Structured field id byte 1 | |||
data[4] = (byte) 0xA9; // Structured field id byte 2 | |||
data[5] = (byte) 0xAD; // Structured field id byte 3 | |||
data[6] = 0x00; // Flags | |||
data[7] = 0x00; // Reserved | |||
data[8] = 0x00; // Reserved | |||
for (int i = 0; i < nameBytes.length; i++) { | |||
data[9 + i] = nameBytes[i]; | |||
} | |||
os.write(data); | |||
} | |||
} |
@@ -38,8 +38,8 @@ public class ImageSizeParameter extends AbstractAFPObject { | |||
* resolution, hsize and vsize. | |||
* @param hresol The horizontal resolution of the image. | |||
* @param vresol The vertical resolution of the image. | |||
* @param hsize The hsize of the image. | |||
* @param vsize The vsize of the vsize. | |||
* @param hsize The horizontal size of the image. | |||
* @param vsize The vertical size of the image. | |||
*/ | |||
public ImageSizeParameter(int hresol, int vresol, int hsize, int vsize) { | |||
this.hRes = hresol; |
@@ -42,12 +42,12 @@ public class IncludePageOverlay extends AbstractNamedAFPObject { | |||
/** | |||
* The x coordinate | |||
*/ | |||
private int xCoor = 0; | |||
private int x = 0; | |||
/** | |||
* The y coordinate | |||
*/ | |||
private int yCoor = 0; | |||
private int y = 0; | |||
/** | |||
* The orientation | |||
@@ -62,11 +62,9 @@ public class IncludePageOverlay extends AbstractNamedAFPObject { | |||
* @param orientation The orientation | |||
*/ | |||
public IncludePageOverlay(String overlayName, int x, int y, int orientation) { | |||
super(overlayName); | |||
xCoor = x; | |||
yCoor = y; | |||
this.x = x; | |||
this.y = y; | |||
setOrientation(orientation); | |||
} | |||
@@ -77,7 +75,6 @@ public class IncludePageOverlay extends AbstractNamedAFPObject { | |||
* The orientation (0,90, 180, 270) | |||
*/ | |||
public void setOrientation(int orientation) { | |||
if (orientation == 0 || orientation == 90 || orientation == 180 | |||
|| orientation == 270) { | |||
this.orientation = orientation; | |||
@@ -85,7 +82,6 @@ public class IncludePageOverlay extends AbstractNamedAFPObject { | |||
throw new IllegalArgumentException( | |||
"The orientation must be one of the values 0, 90, 180, 270"); | |||
} | |||
} | |||
/** | |||
@@ -101,9 +97,9 @@ public class IncludePageOverlay extends AbstractNamedAFPObject { | |||
data[0] = 0x5A; | |||
// Set the total record length | |||
byte[] rl1 = BinaryUtils.convert(24, 2); //Ignore first byte | |||
data[1] = rl1[0]; | |||
data[2] = rl1[1]; | |||
byte[] len = BinaryUtils.convert(24, 2); //Ignore first byte | |||
data[1] = len[0]; | |||
data[2] = len[1]; | |||
// Structured field ID for a IPO | |||
data[3] = (byte) 0xD3; | |||
@@ -115,20 +111,18 @@ public class IncludePageOverlay extends AbstractNamedAFPObject { | |||
data[8] = 0x00; // Reserved | |||
for (int i = 0; i < nameBytes.length; i++) { | |||
data[9 + i] = nameBytes[i]; | |||
} | |||
byte[] r2 = BinaryUtils.convert(xCoor, 3); | |||
data[17] = r2[0]; // x coordinate | |||
data[18] = r2[1]; | |||
data[19] = r2[2]; | |||
byte[] xcoord = BinaryUtils.convert(x, 3); | |||
data[17] = xcoord[0]; // x coordinate | |||
data[18] = xcoord[1]; | |||
data[19] = xcoord[2]; | |||
byte[] r3 = BinaryUtils.convert(yCoor, 3); | |||
data[20] = r3[0]; // y coordinate | |||
data[21] = r3[1]; | |||
data[22] = r3[2]; | |||
byte[] ycoord = BinaryUtils.convert(y, 3); | |||
data[20] = ycoord[0]; // y coordinate | |||
data[21] = ycoord[1]; | |||
data[22] = ycoord[2]; | |||
switch (orientation) { | |||
case 90: | |||
@@ -148,9 +142,6 @@ public class IncludePageOverlay extends AbstractNamedAFPObject { | |||
data[24] = 0x00; | |||
break; | |||
} | |||
os.write(data); | |||
} | |||
} |
@@ -45,24 +45,24 @@ public class IncludePageSegment extends AbstractNamedAFPObject { | |||
/** | |||
* The x position where we need to put this object on the page | |||
*/ | |||
private byte[] xCoor; | |||
private byte[] x; | |||
/** | |||
* The y position where we need to put this object on the page | |||
*/ | |||
private byte[] yCoor; | |||
private byte[] y; | |||
/** | |||
* Constructor for the Include Page Segment | |||
* @param name Name of the page segment | |||
* @param xVal The x position | |||
* @param yVal The y position | |||
* @param x The x position | |||
* @param y The y position | |||
*/ | |||
public IncludePageSegment(String name, int xVal, int yVal) { | |||
public IncludePageSegment(String name, int x, int y) { | |||
super(name); | |||
this.xCoor = BinaryUtils.convert(xVal, 3); | |||
this.yCoor = BinaryUtils.convert(yVal, 3); | |||
this.x = BinaryUtils.convert(x, 3); | |||
this.y = BinaryUtils.convert(y, 3); | |||
} | |||
@@ -93,22 +93,17 @@ public class IncludePageSegment extends AbstractNamedAFPObject { | |||
data[8] = 0x00; // Reserved | |||
for (int i = 0; i < nameBytes.length; i++) { | |||
data[9 + i] = nameBytes[i]; | |||
} | |||
data[17] = xCoor[0]; // x coordinate | |||
data[18] = xCoor[1]; | |||
data[19] = xCoor[2]; | |||
data[17] = x[0]; // x coordinate | |||
data[18] = x[1]; | |||
data[19] = x[2]; | |||
data[20] = yCoor[0]; // y coordinate | |||
data[21] = yCoor[1]; | |||
data[22] = yCoor[2]; | |||
data[20] = y[0]; // y coordinate | |||
data[21] = y[1]; | |||
data[22] = y[2]; | |||
os.write(data); | |||
} | |||
} |
@@ -30,7 +30,6 @@ import org.apache.fop.render.afp.tools.BinaryUtils; | |||
* The Map Page Overlay structured field maps a Resource Local ID to the name of | |||
* a Begin Overlay structured field. A Map Page Overlay structured field may | |||
* contain from one to 254 repeating groups. | |||
* | |||
*/ | |||
public class MapPageOverlay extends AbstractAFPObject { | |||
@@ -68,15 +67,10 @@ public class MapPageOverlay extends AbstractAFPObject { | |||
log.debug("addOverlay():: adding overlay " + name); | |||
} | |||
byte[] data; | |||
try { | |||
data = name.getBytes(AFPConstants.EBCIDIC_ENCODING); | |||
byte[] data = name.getBytes(AFPConstants.EBCIDIC_ENCODING); | |||
overLays.add(data); | |||
} catch (UnsupportedEncodingException usee) { | |||
log.error("addOverlay():: UnsupportedEncodingException translating the name " | |||
+ name); | |||
} | |||
@@ -117,7 +111,6 @@ public class MapPageOverlay extends AbstractAFPObject { | |||
byte olayref = 0x00; | |||
for (int i = 0; i < oLayCount; i++) { | |||
olayref = (byte) (olayref + 1); | |||
data[++pos] = 0x00; |
@@ -68,5 +68,6 @@ public class TIFFRendererConfigurator extends PrintRendererConfigurator { | |||
log.info("TIFF compression set to " + name); | |||
} | |||
} | |||
super.configure(renderer); | |||
} | |||
} |
@@ -0,0 +1,205 @@ | |||
/* | |||
* 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.java2d; | |||
import java.awt.Font; | |||
import java.awt.FontFormatException; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import javax.xml.transform.Source; | |||
import javax.xml.transform.stream.StreamSource; | |||
import org.apache.fop.fonts.CustomFont; | |||
import org.apache.fop.fonts.FontType; | |||
import org.apache.fop.fonts.LazyFont; | |||
import org.apache.fop.fonts.Typeface; | |||
/** | |||
* FontMetricsMapper that delegates most methods to an underlying | |||
* <tt>FontMetrics</tt> instance. This class was designed to allow | |||
* the underlying <tt>java.awt.Font</tt> to be loaded from a | |||
* user-configured file not registered in the current graphics environment. | |||
*/ | |||
public class CustomFontMetricsMapper extends Typeface implements FontMetricsMapper { | |||
/** | |||
* Font metrics for the font this class models. | |||
*/ | |||
private Typeface typeface; | |||
/** | |||
* The font required by the Java2D renderer. | |||
*/ | |||
private java.awt.Font font; | |||
/** | |||
* Maintains the most recently requested size. | |||
*/ | |||
private float size = 1; | |||
/** | |||
* Construction of this class results in the immediate construction. | |||
* of the underlying <tt>java.awt.Font</tt> | |||
* @param fontMetrics | |||
* @throws FontFormatException | |||
* @throws IOException | |||
*/ | |||
public CustomFontMetricsMapper(final CustomFont fontMetrics) | |||
throws FontFormatException, IOException { | |||
this.typeface = fontMetrics; | |||
initialize(fontMetrics.getEmbedFileSource()); | |||
} | |||
/** | |||
* Construction of this class results in the immediate construction | |||
* of the underlying <tt>java.awt.Font</tt> | |||
* @param fontMetrics | |||
* @throws FontFormatException | |||
* @throws IOException | |||
*/ | |||
public CustomFontMetricsMapper(final LazyFont fontMetrics, final Source fontSource) | |||
throws FontFormatException, IOException { | |||
this.typeface = fontMetrics; | |||
initialize(fontSource); | |||
} | |||
private static final int TYPE1_FONT = 1; //Defined in Java 1.5 | |||
/** | |||
* Loads the java.awt.Font | |||
* @param source | |||
* @throws FontFormatException | |||
* @throws IOException | |||
*/ | |||
private void initialize(final Source source) | |||
throws FontFormatException, IOException { | |||
int type = Font.TRUETYPE_FONT; | |||
if (FontType.TYPE1.equals(typeface.getFontType())) { | |||
type = TYPE1_FONT; //Font.TYPE1_FONT; only available in Java 1.5 | |||
} | |||
InputStream is = null; | |||
if (source instanceof StreamSource) { | |||
is = ((StreamSource) source).getInputStream(); | |||
} else if (source.getSystemId() != null) { | |||
is = new java.net.URL(source.getSystemId()).openStream(); | |||
} else { | |||
throw new IllegalArgumentException("No font source provided."); | |||
} | |||
this.font = Font.createFont(type, is); | |||
is.close(); | |||
} | |||
/** {@inheritDoc} */ | |||
public final String getEncoding() { | |||
return null; //Not applicable to Java2D rendering | |||
} | |||
/** {@inheritDoc} */ | |||
public final boolean hasChar(final char c) { | |||
return font.canDisplay(c); | |||
} | |||
/** {@inheritDoc} */ | |||
public final char mapChar(final char c) { | |||
return typeface.mapChar(c); | |||
} | |||
/** {@inheritDoc} */ | |||
public final Font getFont(final int size) { | |||
if (this.size == size) { | |||
return font; | |||
} | |||
this.size = size / 1000f; | |||
font = font.deriveFont(this.size); | |||
return font; | |||
} | |||
/** {@inheritDoc} */ | |||
public final int getAscender(final int size) { | |||
return typeface.getAscender(size); | |||
} | |||
/** {@inheritDoc} */ | |||
public final int getCapHeight(final int size) { | |||
return typeface.getCapHeight(size); | |||
} | |||
/** {@inheritDoc} */ | |||
public final int getDescender(final int size) { | |||
return typeface.getDescender(size); | |||
} | |||
/** {@inheritDoc} */ | |||
public final String getEmbedFontName() { | |||
return typeface.getEmbedFontName(); | |||
} | |||
/** {@inheritDoc} */ | |||
public final Set getFamilyNames() { | |||
return typeface.getFamilyNames(); | |||
} | |||
/** {@inheritDoc} */ | |||
public final String getFontName() { | |||
return typeface.getFontName(); | |||
} | |||
/** {@inheritDoc} */ | |||
public final FontType getFontType() { | |||
return typeface.getFontType(); | |||
} | |||
/** {@inheritDoc} */ | |||
public final String getFullName() { | |||
return typeface.getFullName(); | |||
} | |||
/** {@inheritDoc} */ | |||
public final Map getKerningInfo() { | |||
return typeface.getKerningInfo(); | |||
} | |||
/** {@inheritDoc} */ | |||
public final int getWidth(final int i, final int size) { | |||
return typeface.getWidth(i, size); | |||
} | |||
/** {@inheritDoc} */ | |||
public final int[] getWidths() { | |||
return typeface.getWidths(); | |||
} | |||
/** {@inheritDoc} */ | |||
public final int getXHeight(final int size) { | |||
return typeface.getXHeight(size); | |||
} | |||
/** {@inheritDoc} */ | |||
public final boolean hasKerningInfo() { | |||
return typeface.hasKerningInfo(); | |||
} | |||
} |
@@ -16,183 +16,23 @@ | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.render.java2d; | |||
// Java | |||
import java.awt.Graphics2D; | |||
import java.util.Map; | |||
import java.util.Set; | |||
package org.apache.fop.render.java2d; | |||
import org.apache.fop.fonts.FontMetrics; | |||
import org.apache.fop.fonts.FontType; | |||
import org.apache.fop.fonts.Typeface; | |||
/** | |||
* This class implements org.apache.fop.layout.FontMetrics and | |||
* is added to the hash table in FontInfo. It deferes the | |||
* actual calculation of the metrics to | |||
* Java2DFontMetrics. It only keeps the java name and | |||
* style as member varibles | |||
* Adds method to retrieve the actual <tt>java.awt.Font</tt> | |||
* for use by <tt>Java2DRenderer</tt>s. | |||
*/ | |||
public class FontMetricsMapper extends Typeface implements FontMetrics { | |||
/** | |||
* This is a Java2DFontMetrics that does the real calculation. | |||
* It is only one class that dynamically determines the font-size. | |||
*/ | |||
private static Java2DFontMetrics metric = null; | |||
/** | |||
* The java name of the font. | |||
* # Make the family name immutable. | |||
*/ | |||
private final String family; | |||
/** | |||
* The java style of the font. | |||
* # Make the style immutable. | |||
*/ | |||
private final int style; | |||
/** | |||
* Constructs a new Font-metrics. | |||
* @param family the family name of the font (java value) | |||
* @param style the java type style value of the font | |||
* @param graphics a Graphics2D object - this is needed so | |||
* that we can get an instance of java.awt.FontMetrics | |||
*/ | |||
public FontMetricsMapper(String family, int style, Graphics2D graphics) { | |||
this.family = family; | |||
this.style = style; | |||
if (metric == null) { | |||
metric = new Java2DFontMetrics(graphics); | |||
} | |||
} | |||
/** {@inheritDoc} */ | |||
public String getFontName() { | |||
return family; | |||
} | |||
/** {@inheritDoc} */ | |||
public String getEmbedFontName() { | |||
return getFontName(); | |||
} | |||
/** {@inheritDoc} */ | |||
public String getFullName() { | |||
return getFontName(); | |||
} | |||
/** {@inheritDoc} */ | |||
public Set getFamilyNames() { | |||
Set s = new java.util.HashSet(); | |||
s.add(this.family); | |||
return s; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public FontType getFontType() { | |||
return FontType.OTHER; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public int getMaxAscent(int size) { | |||
return metric.getMaxAscent(family, style, size); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public int getAscender(int size) { | |||
return metric.getAscender(family, style, size); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public int getCapHeight(int size) { | |||
return metric.getCapHeight(family, style, size); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public int getDescender(int size) { | |||
return metric.getDescender(family, style, size); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public int getXHeight(int size) { | |||
return metric.getXHeight(family, style, size); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public int getWidth(int i, int size) { | |||
return metric.width(i, family, style, size); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public int[] getWidths() { | |||
return metric.getWidths(family, style, Java2DFontMetrics.FONT_SIZE); | |||
} | |||
public interface FontMetricsMapper extends FontMetrics { | |||
/** | |||
* Gets a Font instance of the Font that this | |||
* FontMetrics describes in the desired size. | |||
* @param size font size | |||
* @return font with the desired characeristics. | |||
*/ | |||
public java.awt.Font getFont(int size) { | |||
return metric.getFont(family, style, size); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public Map getKerningInfo() { | |||
return java.util.Collections.EMPTY_MAP; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
* @return font with the desired characteristics. | |||
*/ | |||
public boolean hasKerningInfo() { | |||
return false; | |||
} | |||
/** {@inheritDoc} */ | |||
public String getEncoding() { | |||
return null; //Not applicable to Java2D rendering | |||
} | |||
/** {@inheritDoc} */ | |||
public char mapChar(char c) { | |||
return c; | |||
} | |||
/** {@inheritDoc} */ | |||
public boolean hasChar(char c) { | |||
return metric.hasChar(family, style, Java2DFontMetrics.FONT_SIZE, c); | |||
} | |||
java.awt.Font getFont(int size); | |||
} | |||
@@ -5,9 +5,9 @@ | |||
* 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. | |||
@@ -22,14 +22,23 @@ package org.apache.fop.render.java2d; | |||
// FOP | |||
import java.awt.Graphics2D; | |||
import java.awt.GraphicsEnvironment; | |||
import java.util.List; | |||
import java.util.Set; | |||
import javax.xml.transform.Source; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.fonts.CustomFont; | |||
import org.apache.fop.fonts.EmbedFontInfo; | |||
import org.apache.fop.fonts.Font; | |||
import org.apache.fop.fonts.FontInfo; | |||
import org.apache.fop.fonts.FontLoader; | |||
import org.apache.fop.fonts.FontResolver; | |||
import org.apache.fop.fonts.FontTriplet; | |||
import org.apache.fop.fonts.FontUtil; | |||
import org.apache.fop.fonts.LazyFont; | |||
/** | |||
* Sets up the Java2D/AWT fonts. It is similar to | |||
@@ -41,18 +50,18 @@ public class FontSetup { | |||
/** logging instance */ | |||
protected static Log log = LogFactory.getLog(FontSetup.class); | |||
private static final int LAST_PREDEFINED_FONT_NUMBER = 14; | |||
private static final Set HARDCODED_FONT_NAMES; | |||
static { | |||
HARDCODED_FONT_NAMES = new java.util.HashSet(); | |||
HARDCODED_FONT_NAMES.add("any"); | |||
HARDCODED_FONT_NAMES.add("sans-serif"); | |||
HARDCODED_FONT_NAMES.add("serif"); | |||
HARDCODED_FONT_NAMES.add("monospace"); | |||
HARDCODED_FONT_NAMES.add("Helvetica"); | |||
HARDCODED_FONT_NAMES.add("Times"); | |||
HARDCODED_FONT_NAMES.add("Courier"); | |||
@@ -62,7 +71,7 @@ public class FontSetup { | |||
HARDCODED_FONT_NAMES.add("Times-Roman"); | |||
HARDCODED_FONT_NAMES.add("Computer-Modern-Typewriter"); | |||
} | |||
/** | |||
* Sets up the font info object. | |||
* | |||
@@ -70,9 +79,12 @@ public class FontSetup { | |||
* triplets for lookup. | |||
* | |||
* @param fontInfo the font info object to set up | |||
* @param graphics needed for acces to font metrics | |||
* @param configuredFontList of fop config fonts | |||
* @param resolver for resolving the font file URI | |||
* @param graphics needed for access to font metrics | |||
*/ | |||
public static void setup(FontInfo fontInfo, Graphics2D graphics) { | |||
public static void setup(FontInfo fontInfo, List configuredFontList, | |||
FontResolver resolver, Graphics2D graphics) { | |||
FontMetricsMapper metric; | |||
int normal, bold, bolditalic, italic; | |||
@@ -87,47 +99,47 @@ public class FontSetup { | |||
italic = java.awt.Font.ITALIC; | |||
bolditalic = java.awt.Font.BOLD + java.awt.Font.ITALIC; | |||
metric = new FontMetricsMapper("SansSerif", normal, graphics); | |||
metric = new SystemFontMetricsMapper("SansSerif", normal, graphics); | |||
// --> goes to F1 | |||
fontInfo.addMetrics("F1", metric); | |||
metric = new FontMetricsMapper("SansSerif", italic, graphics); | |||
metric = new SystemFontMetricsMapper("SansSerif", italic, graphics); | |||
// --> goes to F2 | |||
fontInfo.addMetrics("F2", metric); | |||
metric = new FontMetricsMapper("SansSerif", bold, graphics); | |||
metric = new SystemFontMetricsMapper("SansSerif", bold, graphics); | |||
// --> goes to F3 | |||
fontInfo.addMetrics("F3", metric); | |||
metric = new FontMetricsMapper("SansSerif", bolditalic, graphics); | |||
metric = new SystemFontMetricsMapper("SansSerif", bolditalic, graphics); | |||
// --> goes to F4 | |||
fontInfo.addMetrics("F4", metric); | |||
metric = new FontMetricsMapper("Serif", normal, graphics); | |||
metric = new SystemFontMetricsMapper("Serif", normal, graphics); | |||
// --> goes to F5 | |||
fontInfo.addMetrics("F5", metric); | |||
metric = new FontMetricsMapper("Serif", italic, graphics); | |||
metric = new SystemFontMetricsMapper("Serif", italic, graphics); | |||
// --> goes to F6 | |||
fontInfo.addMetrics("F6", metric); | |||
metric = new FontMetricsMapper("Serif", bold, graphics); | |||
metric = new SystemFontMetricsMapper("Serif", bold, graphics); | |||
// --> goes to F7 | |||
fontInfo.addMetrics("F7", metric); | |||
metric = new FontMetricsMapper("Serif", bolditalic, graphics); | |||
metric = new SystemFontMetricsMapper("Serif", bolditalic, graphics); | |||
// --> goes to F8 | |||
fontInfo.addMetrics("F8", metric); | |||
metric = new FontMetricsMapper("MonoSpaced", normal, graphics); | |||
metric = new SystemFontMetricsMapper("MonoSpaced", normal, graphics); | |||
// --> goes to F9 | |||
fontInfo.addMetrics("F9", metric); | |||
metric = new FontMetricsMapper("MonoSpaced", italic, graphics); | |||
metric = new SystemFontMetricsMapper("MonoSpaced", italic, graphics); | |||
// --> goes to F10 | |||
fontInfo.addMetrics("F10", metric); | |||
metric = new FontMetricsMapper("MonoSpaced", bold, graphics); | |||
metric = new SystemFontMetricsMapper("MonoSpaced", bold, graphics); | |||
// --> goes to F11 | |||
fontInfo.addMetrics("F11", metric); | |||
metric = new FontMetricsMapper("MonoSpaced", bolditalic, graphics); | |||
metric = new SystemFontMetricsMapper("MonoSpaced", bolditalic, graphics); | |||
// --> goes to F12 | |||
fontInfo.addMetrics("F12", metric); | |||
metric = new FontMetricsMapper("Serif", normal, graphics); | |||
metric = new SystemFontMetricsMapper("Serif", normal, graphics); | |||
//"Symbol" doesn't seem to work here, but "Serif" does the job just fine. *shrug* | |||
// --> goes to F13 and F14 | |||
fontInfo.addMetrics("F13", metric); | |||
@@ -206,31 +218,32 @@ public class FontSetup { | |||
fontInfo.addFontProperties("F8", "Times Roman", "italic", Font.WEIGHT_BOLD); | |||
fontInfo.addFontProperties("F9", "Computer-Modern-Typewriter", | |||
"normal", Font.WEIGHT_NORMAL); | |||
configureInstalledAWTFonts(fontInfo, graphics, LAST_PREDEFINED_FONT_NUMBER + 1); | |||
int lastNum = configureInstalledAWTFonts(fontInfo, graphics, LAST_PREDEFINED_FONT_NUMBER + 1); | |||
addConfiguredFonts(fontInfo, configuredFontList, resolver, lastNum++); | |||
} | |||
private static void configureInstalledAWTFonts(FontInfo fontInfo, Graphics2D graphics, | |||
private static int configureInstalledAWTFonts(FontInfo fontInfo, Graphics2D graphics, | |||
int startNumber) { | |||
int num = startNumber; | |||
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); | |||
java.awt.Font[] fonts = env.getAllFonts(); | |||
for (int i = 0; i < fonts.length; i++) { | |||
java.awt.Font f = fonts[i]; | |||
if (HARDCODED_FONT_NAMES.contains(f.getName())) { | |||
continue; //skip | |||
} | |||
if (log.isTraceEnabled()) { | |||
log.trace("AWT Font: " + f.getFontName() | |||
+ ", family: " + f.getFamily() | |||
+ ", PS: " + f.getPSName() | |||
log.trace("AWT Font: " + f.getFontName() | |||
+ ", family: " + f.getFamily() | |||
+ ", PS: " + f.getPSName() | |||
+ ", Name: " + f.getName() | |||
+ ", Angle: " + f.getItalicAngle() | |||
+ ", Style: " + f.getStyle()); | |||
} | |||
String searchName = FontUtil.stripWhiteSpace(f.getName()).toLowerCase(); | |||
String guessedStyle = FontUtil.guessStyle(searchName); | |||
int guessedWeight = FontUtil.guessWeight(searchName); | |||
@@ -239,7 +252,7 @@ public class FontSetup { | |||
String fontKey = "F" + num; | |||
int style = convertToAWTFontStyle(guessedStyle, guessedWeight); | |||
addFontMetricsMapper(fontInfo, f.getName(), fontKey, graphics, style); | |||
//Register appropriate font triplets matching the font. Two different strategies: | |||
//Example: "Arial Bold", normal, normal | |||
addFontTriplet(fontInfo, f.getName(), | |||
@@ -250,9 +263,69 @@ public class FontSetup { | |||
guessedStyle, guessedWeight, fontKey); | |||
} | |||
} | |||
return num; | |||
} | |||
/** | |||
* Add fonts from configuration file starting with internal name F<num>. | |||
* | |||
* @param fontInfo the font info object to set up | |||
* @param fontList a list of EmbedFontInfo objects | |||
* @param num starting index for internal font numbering | |||
* @param resolver the font resolver | |||
*/ | |||
private static void addConfiguredFonts(FontInfo fontInfo, List fontList, FontResolver resolver, int num) { | |||
if (fontList == null || fontList.size() < 1) { | |||
log.debug("No user configured fonts found."); | |||
return; | |||
} | |||
if (resolver == null) { | |||
// Ensure that we have minimal font resolution capabilities | |||
resolver = org.apache.fop.fonts.FontSetup | |||
.createMinimalFontResolver(); | |||
} | |||
String internalName = null; | |||
for (int i = 0; i < fontList.size(); i++) { | |||
EmbedFontInfo configFontInfo = (EmbedFontInfo) fontList.get(i); | |||
String fontFile = configFontInfo.getEmbedFile(); | |||
internalName = "F" + num; | |||
num++; | |||
try { | |||
FontMetricsMapper font = null; | |||
String metricsUrl = configFontInfo.getMetricsFile(); | |||
// If the user specified an XML-based metrics file, we'll use it | |||
// Otherwise, calculate metrics directly from the font file. | |||
if (metricsUrl != null) { | |||
LazyFont fontMetrics = new LazyFont(configFontInfo, resolver); | |||
Source fontSource = resolver.resolve(configFontInfo.getEmbedFile()); | |||
font = new CustomFontMetricsMapper(fontMetrics, fontSource); | |||
} else { | |||
CustomFont fontMetrics = FontLoader.loadFont(fontFile, resolver); | |||
font = new CustomFontMetricsMapper(fontMetrics); | |||
} | |||
fontInfo.addMetrics(internalName, font); | |||
List triplets = configFontInfo.getFontTriplets(); | |||
for (int c = 0; c < triplets.size(); c++) { | |||
FontTriplet triplet = (FontTriplet) triplets.get(c); | |||
if (log.isDebugEnabled()) { | |||
log.debug("Registering: " + triplet + " under " + internalName); | |||
} | |||
fontInfo.addFontProperties(internalName, triplet); | |||
} | |||
} catch (Exception e) { | |||
log.warn("Unable to load custom font from file '" + fontFile + "'", e); | |||
} | |||
} | |||
} | |||
private static void addFontTriplet(FontInfo fontInfo, String fontName, String fontStyle, | |||
int fontWeight, String fontKey) { | |||
FontTriplet triplet = FontInfo.createFontKey(fontName, fontStyle, fontWeight); | |||
@@ -261,7 +334,7 @@ public class FontSetup { | |||
private static void addFontMetricsMapper(FontInfo fontInfo, String family, String fontKey, | |||
Graphics2D graphics, int style) { | |||
FontMetricsMapper metric = new FontMetricsMapper(family, style, graphics); | |||
FontMetricsMapper metric = new SystemFontMetricsMapper(family, style, graphics); | |||
fontInfo.addMetrics(fontKey, metric); | |||
} | |||
@@ -275,6 +348,6 @@ public class FontSetup { | |||
} | |||
return style; | |||
} | |||
} | |||
@@ -176,7 +176,7 @@ public abstract class Java2DRenderer extends AbstractPathOrientedRenderer implem | |||
//The next line is important to get accurate font metrics! | |||
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, | |||
RenderingHints.VALUE_FRACTIONALMETRICS_ON); | |||
FontSetup.setup(fontInfo, g); | |||
FontSetup.setup(fontInfo, fontList, fontResolver, g); | |||
} | |||
/** {@inheritDoc} */ |
@@ -53,5 +53,6 @@ public class Java2DRendererConfigurator extends PrintRendererConfigurator { | |||
java2dRenderer.setTransparentPageBackground("true".equalsIgnoreCase(value)); | |||
} | |||
} | |||
super.configure(renderer); | |||
} | |||
} |
@@ -0,0 +1,194 @@ | |||
/* | |||
* 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.java2d; | |||
// Java | |||
import java.awt.Graphics2D; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import org.apache.fop.fonts.FontType; | |||
import org.apache.fop.fonts.Typeface; | |||
/** | |||
* This class implements org.apache.fop.layout.FontMetrics and | |||
* is added to the hash table in FontInfo. It deferes the | |||
* actual calculation of the metrics to | |||
* Java2DFontMetrics. It only keeps the java name and | |||
* style as member varibles | |||
*/ | |||
public class SystemFontMetricsMapper extends Typeface implements FontMetricsMapper { | |||
/** | |||
* This is a Java2DFontMetrics that does the real calculation. | |||
* It is only one class that dynamically determines the font-size. | |||
*/ | |||
private static Java2DFontMetrics metric = null; | |||
/** | |||
* The java name of the font. | |||
* # Make the family name immutable. | |||
*/ | |||
private final String family; | |||
/** | |||
* The java style of the font. | |||
* # Make the style immutable. | |||
*/ | |||
private final int style; | |||
/** | |||
* Constructs a new Font-metrics. | |||
* @param family the family name of the font (java value) | |||
* @param style the java type style value of the font | |||
* @param graphics a Graphics2D object - this is needed so | |||
* that we can get an instance of java.awt.FontMetrics | |||
*/ | |||
public SystemFontMetricsMapper(String family, int style, Graphics2D graphics) { | |||
this.family = family; | |||
this.style = style; | |||
if (metric == null) { | |||
metric = new Java2DFontMetrics(graphics); | |||
} | |||
} | |||
/** {@inheritDoc} */ | |||
public String getFontName() { | |||
return family; | |||
} | |||
/** {@inheritDoc} */ | |||
public String getEmbedFontName() { | |||
return getFontName(); | |||
} | |||
/** {@inheritDoc} */ | |||
public String getFullName() { | |||
return getFontName(); | |||
} | |||
/** {@inheritDoc} */ | |||
public Set getFamilyNames() { | |||
Set s = new java.util.HashSet(); | |||
s.add(this.family); | |||
return s; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public FontType getFontType() { | |||
return FontType.OTHER; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public int getMaxAscent(int size) { | |||
return metric.getMaxAscent(family, style, size); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public int getAscender(int size) { | |||
return metric.getAscender(family, style, size); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public int getCapHeight(int size) { | |||
return metric.getCapHeight(family, style, size); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public int getDescender(int size) { | |||
return metric.getDescender(family, style, size); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public int getXHeight(int size) { | |||
return metric.getXHeight(family, style, size); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public int getWidth(int i, int size) { | |||
return metric.width(i, family, style, size); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public int[] getWidths() { | |||
return metric.getWidths(family, style, Java2DFontMetrics.FONT_SIZE); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public java.awt.Font getFont(int size) { | |||
return metric.getFont(family, style, size); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public Map getKerningInfo() { | |||
return java.util.Collections.EMPTY_MAP; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public boolean hasKerningInfo() { | |||
return false; | |||
} | |||
/** {@inheritDoc} */ | |||
public String getEncoding() { | |||
return null; //Not applicable to Java2D rendering | |||
} | |||
/** {@inheritDoc} */ | |||
public char mapChar(char c) { | |||
return c; | |||
} | |||
/** {@inheritDoc} */ | |||
public boolean hasChar(char c) { | |||
return metric.hasChar(family, style, Java2DFontMetrics.FONT_SIZE, c); | |||
} | |||
} | |||
@@ -192,7 +192,7 @@ public class PCLRenderer extends PrintRenderer { | |||
//The next line is important to get accurate font metrics! | |||
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, | |||
RenderingHints.VALUE_FRACTIONALMETRICS_ON); | |||
FontSetup.setup(fontInfo, g); | |||
FontSetup.setup(fontInfo, fontList, fontResolver, g); | |||
} | |||
/** |
@@ -20,6 +20,7 @@ | |||
package org.apache.fop.render.pcl; | |||
import org.apache.avalon.framework.configuration.Configuration; | |||
import org.apache.fop.apps.FOPException; | |||
import org.apache.fop.apps.FOUserAgent; | |||
import org.apache.fop.render.PrintRendererConfigurator; | |||
@@ -73,5 +74,6 @@ public class PCLRendererConfigurator extends PrintRendererConfigurator { | |||
pclRenderer.setPJLDisabled(cfg.getChild("disable-pjl").getValueAsBoolean(false)); | |||
} | |||
super.configure(renderer); | |||
} | |||
} |
@@ -39,9 +39,11 @@ import java.util.Map; | |||
import javax.xml.transform.Source; | |||
import javax.xml.transform.stream.StreamSource; | |||
import org.w3c.dom.Document; | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.commons.io.output.CountingOutputStream; | |||
import org.apache.xmlgraphics.image.loader.ImageException; | |||
import org.apache.xmlgraphics.image.loader.ImageInfo; | |||
import org.apache.xmlgraphics.image.loader.ImageManager; | |||
import org.apache.xmlgraphics.image.loader.ImageSessionContext; | |||
@@ -83,6 +85,7 @@ import org.apache.fop.pdf.PDFAction; | |||
import org.apache.fop.pdf.PDFAnnotList; | |||
import org.apache.fop.pdf.PDFColor; | |||
import org.apache.fop.pdf.PDFConformanceException; | |||
import org.apache.fop.pdf.PDFDictionary; | |||
import org.apache.fop.pdf.PDFDocument; | |||
import org.apache.fop.pdf.PDFEncryptionManager; | |||
import org.apache.fop.pdf.PDFEncryptionParams; | |||
@@ -95,9 +98,11 @@ import org.apache.fop.pdf.PDFInfo; | |||
import org.apache.fop.pdf.PDFLink; | |||
import org.apache.fop.pdf.PDFMetadata; | |||
import org.apache.fop.pdf.PDFNumber; | |||
import org.apache.fop.pdf.PDFNumsArray; | |||
import org.apache.fop.pdf.PDFOutline; | |||
import org.apache.fop.pdf.PDFOutputIntent; | |||
import org.apache.fop.pdf.PDFPage; | |||
import org.apache.fop.pdf.PDFPageLabels; | |||
import org.apache.fop.pdf.PDFResourceContext; | |||
import org.apache.fop.pdf.PDFResources; | |||
import org.apache.fop.pdf.PDFState; | |||
@@ -361,7 +366,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { | |||
this.pdfDoc.getInfo().setTitle(userAgent.getTitle()); | |||
this.pdfDoc.getInfo().setKeywords(userAgent.getKeywords()); | |||
this.pdfDoc.setFilterMap(filterMap); | |||
this.pdfDoc.outputHeader(stream); | |||
this.pdfDoc.outputHeader(ostream); | |||
//Setup encryption if necessary | |||
PDFEncryptionManager.setupPDFEncryption(encryptionParams, this.pdfDoc); | |||
@@ -721,6 +726,20 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { | |||
page.getPageIndex()); | |||
pageReferences.put(page.getKey(), currentPage.referencePDF()); | |||
pvReferences.put(page.getKey(), page); | |||
//Produce page labels | |||
PDFPageLabels pageLabels = this.pdfDoc.getRoot().getPageLabels(); | |||
if (pageLabels == null) { | |||
//Set up PageLabels | |||
pageLabels = this.pdfDoc.getFactory().makePageLabels(); | |||
this.pdfDoc.getRoot().setPageLabels(pageLabels); | |||
} | |||
PDFNumsArray nums = pageLabels.getNums(); | |||
PDFDictionary dict = new PDFDictionary(nums); | |||
dict.put("P", page.getPageNumberString()); | |||
//TODO If the sequence of generated page numbers were inspected, this could be | |||
//expressed in a more space-efficient way | |||
nums.put(page.getPageIndex(), dict); | |||
} | |||
/** |
@@ -36,6 +36,8 @@ import java.util.Map; | |||
import javax.xml.transform.Source; | |||
import org.w3c.dom.Document; | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
@@ -168,6 +170,9 @@ public class PSRenderer extends AbstractPathOrientedRenderer | |||
/** Whether or not Dublin Core Standard (dsc) compliant output is enforced */ | |||
private boolean dscCompliant = true; | |||
/** Is used to determine the document's bounding box */ | |||
private Rectangle2D documentBoundingBox; | |||
/** This is a collection holding all document header comments */ | |||
private Collection headerComments; | |||
@@ -913,6 +918,9 @@ public class PSRenderer extends AbstractPathOrientedRenderer | |||
gen.writeDSCComment(DSCConstants.CREATION_DATE, new Object[] {new java.util.Date()}); | |||
gen.writeDSCComment(DSCConstants.LANGUAGE_LEVEL, new Integer(gen.getPSLevel())); | |||
gen.writeDSCComment(DSCConstants.PAGES, new Object[] {DSCConstants.ATEND}); | |||
gen.writeDSCComment(DSCConstants.BBOX, DSCConstants.ATEND); | |||
gen.writeDSCComment(DSCConstants.HIRES_BBOX, DSCConstants.ATEND); | |||
this.documentBoundingBox = new Rectangle2D.Double(); | |||
gen.writeDSCComment(DSCConstants.DOCUMENT_SUPPLIED_RESOURCES, | |||
new Object[] {DSCConstants.ATEND}); | |||
if (headerComments != null) { | |||
@@ -970,6 +978,8 @@ public class PSRenderer extends AbstractPathOrientedRenderer | |||
footerComments.clear(); | |||
} | |||
gen.writeDSCComment(DSCConstants.PAGES, new Integer(this.currentPageNumber)); | |||
new DSCCommentBoundingBox(this.documentBoundingBox).generate(gen); | |||
new DSCCommentHiResBoundingBox(this.documentBoundingBox).generate(gen); | |||
gen.getResourceTracker().writeResources(false, gen); | |||
gen.writeDSCComment(DSCConstants.EOF); | |||
gen.flush(); | |||
@@ -1000,7 +1010,8 @@ public class PSRenderer extends AbstractPathOrientedRenderer | |||
try { | |||
try { | |||
ResourceHandler.process(this.userAgent, in, this.outputStream, | |||
this.fontInfo, resTracker, this.formResources, this.currentPageNumber); | |||
this.fontInfo, resTracker, this.formResources, | |||
this.currentPageNumber, this.documentBoundingBox); | |||
this.outputStream.flush(); | |||
} catch (DSCException e) { | |||
throw new RuntimeException(e.getMessage()); | |||
@@ -1168,7 +1179,9 @@ public class PSRenderer extends AbstractPathOrientedRenderer | |||
log.error(e.getMessage()); | |||
} | |||
final Integer zero = new Integer(0); | |||
Rectangle2D pageBoundingBox = new Rectangle2D.Double(); | |||
if (rotate) { | |||
pageBoundingBox.setRect(0, 0, pageHeight, pageWidth); | |||
gen.writeDSCComment(DSCConstants.PAGE_BBOX, new Object[] { | |||
zero, zero, new Long(Math.round(pageHeight)), | |||
new Long(Math.round(pageWidth)) }); | |||
@@ -1177,6 +1190,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer | |||
new Double(pageWidth) }); | |||
gen.writeDSCComment(DSCConstants.PAGE_ORIENTATION, "Landscape"); | |||
} else { | |||
pageBoundingBox.setRect(0, 0, pageWidth, pageHeight); | |||
gen.writeDSCComment(DSCConstants.PAGE_BBOX, new Object[] { | |||
zero, zero, new Long(Math.round(pageWidth)), | |||
new Long(Math.round(pageHeight)) }); | |||
@@ -1188,6 +1202,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer | |||
"Portrait"); | |||
} | |||
} | |||
this.documentBoundingBox.add(pageBoundingBox); | |||
gen.writeDSCComment(DSCConstants.PAGE_RESOURCES, | |||
new Object[] {DSCConstants.ATEND}); | |||
@@ -21,6 +21,7 @@ package org.apache.fop.render.ps; | |||
import java.awt.geom.Dimension2D; | |||
import java.awt.image.RenderedImage; | |||
import java.awt.geom.Rectangle2D; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
@@ -54,8 +55,10 @@ import org.apache.xmlgraphics.ps.dsc.DSCParserConstants; | |||
import org.apache.xmlgraphics.ps.dsc.DefaultNestedDocumentHandler; | |||
import org.apache.xmlgraphics.ps.dsc.ResourceTracker; | |||
import org.apache.xmlgraphics.ps.dsc.events.DSCComment; | |||
import org.apache.xmlgraphics.ps.dsc.events.DSCCommentBoundingBox; | |||
import org.apache.xmlgraphics.ps.dsc.events.DSCCommentDocumentNeededResources; | |||
import org.apache.xmlgraphics.ps.dsc.events.DSCCommentDocumentSuppliedResources; | |||
import org.apache.xmlgraphics.ps.dsc.events.DSCCommentHiResBoundingBox; | |||
import org.apache.xmlgraphics.ps.dsc.events.DSCCommentLanguageLevel; | |||
import org.apache.xmlgraphics.ps.dsc.events.DSCCommentPage; | |||
import org.apache.xmlgraphics.ps.dsc.events.DSCCommentPages; | |||
@@ -85,11 +88,14 @@ public class ResourceHandler implements DSCParserConstants, PSSupportedFlavors { | |||
* @param resTracker the resource tracker to use | |||
* @param formResources Contains all forms used by this document (maintained by PSRenderer) | |||
* @param pageCount the number of pages (given here because PSRenderer writes an "(atend)") | |||
* @param documentBoundingBox the document's bounding box | |||
* (given here because PSRenderer writes an "(atend)") | |||
* @throws DSCException If there's an error in the DSC structure of the PS file | |||
* @throws IOException In case of an I/O error | |||
*/ | |||
public static void process(FOUserAgent userAgent, InputStream in, OutputStream out, | |||
FontInfo fontInfo, ResourceTracker resTracker, Map formResources, int pageCount) | |||
FontInfo fontInfo, ResourceTracker resTracker, Map formResources, | |||
int pageCount, Rectangle2D documentBoundingBox) | |||
throws DSCException, IOException { | |||
DSCParser parser = new DSCParser(in); | |||
PSGenerator gen = new PSGenerator(out); | |||
@@ -104,6 +110,8 @@ public class ResourceHandler implements DSCParserConstants, PSSupportedFlavors { | |||
{ | |||
//We rewrite those as part of the processing | |||
filtered.add(DSCConstants.PAGES); | |||
filtered.add(DSCConstants.BBOX); | |||
filtered.add(DSCConstants.HIRES_BBOX); | |||
filtered.add(DSCConstants.DOCUMENT_NEEDED_RESOURCES); | |||
filtered.add(DSCConstants.DOCUMENT_SUPPLIED_RESOURCES); | |||
} | |||
@@ -127,6 +135,8 @@ public class ResourceHandler implements DSCParserConstants, PSSupportedFlavors { | |||
//Set number of pages | |||
DSCCommentPages pages = new DSCCommentPages(pageCount); | |||
pages.generate(gen); | |||
new DSCCommentBoundingBox(documentBoundingBox).generate(gen); | |||
new DSCCommentHiResBoundingBox(documentBoundingBox).generate(gen); | |||
PSFontUtils.determineSuppliedFonts(resTracker, fontInfo, fontInfo.getUsedFonts()); | |||
registerSuppliedForms(resTracker, formResources); |
@@ -19,12 +19,14 @@ | |||
package org.apache.fop.render.ps.extensions; | |||
import org.apache.fop.fo.extensions.ExtensionAttachment; | |||
import org.apache.fop.util.XMLizable; | |||
import org.xml.sax.ContentHandler; | |||
import org.xml.sax.SAXException; | |||
import org.xml.sax.helpers.AttributesImpl; | |||
import org.apache.xmlgraphics.util.XMLizable; | |||
import org.apache.fop.fo.extensions.ExtensionAttachment; | |||
/** | |||
* This is the pass-through value object for the PostScript extension. | |||
*/ |
@@ -96,7 +96,7 @@ public class PSSetPageDevice extends PSExtensionAttachment { | |||
* Generates SAX events representing the object's state. | |||
* @param handler ContentHandler instance to send the SAX events to | |||
* @throws SAXException if there's a problem generating the SAX events | |||
* @see org.apache.fop.util.XMLizable#toSAX(org.xml.sax.ContentHandler) | |||
* @see org.apache.xmlgraphics.util.XMLizable#toSAX(org.xml.sax.ContentHandler) | |||
*/ | |||
public void toSAX(ContentHandler handler) throws SAXException { | |||
AttributesImpl atts = new AttributesImpl(); |
@@ -41,7 +41,8 @@ import org.xml.sax.SAXException; | |||
import org.xml.sax.ext.LexicalHandler; | |||
import org.xml.sax.helpers.AttributesImpl; | |||
import org.apache.fop.util.QName; | |||
import org.apache.xmlgraphics.util.XMLizable; | |||
import org.apache.fop.apps.FOPException; | |||
import org.apache.fop.apps.FOUserAgent; | |||
import org.apache.fop.apps.MimeConstants; | |||
@@ -51,21 +52,22 @@ import org.apache.fop.area.BeforeFloat; | |||
import org.apache.fop.area.Block; | |||
import org.apache.fop.area.BlockViewport; | |||
import org.apache.fop.area.BodyRegion; | |||
import org.apache.fop.area.BookmarkData; | |||
import org.apache.fop.area.CTM; | |||
import org.apache.fop.area.DestinationData; | |||
import org.apache.fop.area.Footnote; | |||
import org.apache.fop.area.LineArea; | |||
import org.apache.fop.area.MainReference; | |||
import org.apache.fop.area.NormalFlow; | |||
import org.apache.fop.area.OffDocumentExtensionAttachment; | |||
import org.apache.fop.area.OffDocumentItem; | |||
import org.apache.fop.area.BookmarkData; | |||
import org.apache.fop.area.PageViewport; | |||
import org.apache.fop.area.RegionReference; | |||
import org.apache.fop.area.RegionViewport; | |||
import org.apache.fop.area.Span; | |||
import org.apache.fop.area.Trait; | |||
import org.apache.fop.area.Trait.InternalLink; | |||
import org.apache.fop.area.Trait.Background; | |||
import org.apache.fop.area.Trait.InternalLink; | |||
import org.apache.fop.area.inline.Container; | |||
import org.apache.fop.area.inline.ForeignObject; | |||
import org.apache.fop.area.inline.Image; | |||
@@ -87,7 +89,7 @@ import org.apache.fop.render.Renderer; | |||
import org.apache.fop.render.RendererContext; | |||
import org.apache.fop.render.XMLHandler; | |||
import org.apache.fop.util.ColorUtil; | |||
import org.apache.fop.util.XMLizable; | |||
import org.apache.fop.util.QName; | |||
/** | |||
* Renderer that renders areas to XML for debugging purposes. | |||
@@ -419,6 +421,8 @@ public class XMLRenderer extends PrintRenderer { | |||
public void processOffDocumentItem(OffDocumentItem oDI) { | |||
if (oDI instanceof BookmarkData) { | |||
renderBookmarkTree((BookmarkData) oDI); | |||
} else if (oDI instanceof DestinationData) { | |||
renderDestination((DestinationData) oDI); | |||
} else if (oDI instanceof OffDocumentExtensionAttachment) { | |||
ExtensionAttachment attachment = ((OffDocumentExtensionAttachment)oDI).getAttachment(); | |||
if (extensionAttachments == null) { | |||
@@ -466,8 +470,23 @@ public class XMLRenderer extends PrintRenderer { | |||
} | |||
/** | |||
* {@inheritDoc} | |||
* Renders a DestinationData object (named destination) | |||
* @param destination the destination object | |||
*/ | |||
protected void renderDestination(DestinationData destination) { | |||
if (destination.getWhenToProcess() == OffDocumentItem.END_OF_DOC) { | |||
endPageSequence(); | |||
} | |||
atts.clear(); | |||
PageViewport pv = destination.getPageViewport(); | |||
String pvKey = pv == null ? null : pv.getKey(); | |||
addAttribute("internal-link", | |||
InternalLink.makeXMLAttribute(pvKey, destination.getIDRef())); | |||
startElement("destination", atts); | |||
endElement("destination"); | |||
} | |||
/** {@inheritDoc} */ | |||
public void startRenderer(OutputStream outputStream) | |||
throws IOException { | |||
log.debug("Rendering areas to Area Tree XML"); |
@@ -25,20 +25,11 @@ package org.apache.fop.util; | |||
* src/java/org/apache/excalibur/xml/sax/XMLizable.java | |||
*/ | |||
import org.xml.sax.ContentHandler; | |||
import org.xml.sax.SAXException; | |||
/** | |||
* This interface can be implemented by classes willing to provide an XML representation | |||
* of their current state as SAX events. | |||
* @deprecated Use the interface in Apache XML Graphics Commons instead. | |||
*/ | |||
public interface XMLizable { | |||
/** | |||
* Generates SAX events representing the object's state. | |||
* @param handler ContentHandler instance to send the SAX events to | |||
* @throws SAXException if there's a problem generating the SAX events | |||
*/ | |||
void toSAX(ContentHandler handler) throws SAXException; | |||
public interface XMLizable extends org.apache.xmlgraphics.util.XMLizable { | |||
} |
@@ -28,6 +28,18 @@ | |||
<changes> | |||
<release version="FOP Trunk"> | |||
<action context="Code" dev="JM" type="fix"> | |||
Fixed generation of named destinations so it works when the PDF document is encrypted. | |||
</action> | |||
<action context="Code" dev="JM" type="add"> | |||
PostScript output now generates the bounding box DSC comments for the whole document. | |||
</action> | |||
<action context="Code" dev="JM" type="add"> | |||
Added support for PDF page labels. | |||
</action> | |||
<action context="Code" dev="JM" type="add" fixes-bug="44176" due-to="Patrick Jaromin"> | |||
Added support for custom fonts in Java2DRenderer and derived renderers. | |||
</action> | |||
<action context="Code" dev="JM" type="add"> | |||
Added new extension element: fox:external-document. It allows to add whole documents | |||
such as multi-page TIFF images to be inserted as peers to a page-sequence. Each image |
@@ -0,0 +1,61 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!-- | |||
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$ --> | |||
<testcase> | |||
<info> | |||
<p> | |||
This test checks the basics of fox:destination. | |||
</p> | |||
</info> | |||
<fo> | |||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" | |||
xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"> | |||
<fo:layout-master-set> | |||
<fo:simple-page-master master-name="normal" page-width="5in" page-height="5in"> | |||
<fo:region-body/> | |||
</fo:simple-page-master> | |||
</fo:layout-master-set> | |||
<fox:destination internal-destination="chapter1"/> | |||
<fox:destination internal-destination="chapter2"/> | |||
<fox:destination internal-destination="chapter2-sec1"/> | |||
<fo:page-sequence id="page-sequence" master-reference="normal"> | |||
<fo:flow flow-name="xsl-region-body"> | |||
<fo:block id="chapter1" font-weight="bold" font-size="larger">Chapter 1</fo:block> | |||
<fo:block>Blah blah bla.</fo:block> | |||
<fo:block id="chapter2" font-weight="bold" font-size="larger" break-before="page">Chapter 2</fo:block> | |||
<fo:block>Blah blah bla.</fo:block> | |||
<fo:block id="chapter2-sec1" font-weight="bold">Section 1</fo:block> | |||
<fo:block>Blah blah bla.</fo:block> | |||
<fo:block id="chapter2-sec2" font-weight="bold"><fox:destination internal-destination="chapter2-sec2"/>Section 2</fo:block> | |||
<fo:block>Blah blah bla.</fo:block> | |||
</fo:flow> | |||
</fo:page-sequence> | |||
</fo:root> | |||
</fo> | |||
<checks> | |||
<eval expected="1" xpath="count(/areaTree/pageSequence)"/> | |||
<eval expected="2" xpath="count(//pageViewport)"/> | |||
<eval expected="4" xpath="count(//destination)"/> | |||
<eval expected="(P1,chapter1)" xpath="//destination[1]/@internal-link"/> | |||
<eval expected="(P2,chapter2)" xpath="//destination[2]/@internal-link"/> | |||
<eval expected="(P2,chapter2-sec1)" xpath="//destination[3]/@internal-link"/> | |||
<eval expected="(P2,chapter2-sec2)" xpath="//destination[4]/@internal-link"/> | |||
</checks> | |||
</testcase> |