You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fop-commits@xmlgraphics.apache.org by sp...@apache.org on 2007/09/25 10:41:56 UTC
svn commit: r579125 [3/3] - in
/xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking: ./
src/documentation/ src/documentation/content/
src/documentation/content/xdocs/trunk/ src/java/org/apache/fop/apps/
src/java/org/apache/fop/area/ src/java/o...
Modified: xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/pdf/PDFRoot.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/pdf/PDFRoot.java?rev=579125&r1=579124&r2=579125&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/pdf/PDFRoot.java (original)
+++ xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/pdf/PDFRoot.java Tue Sep 25 01:41:51 2007
@@ -20,7 +20,6 @@
package org.apache.fop.pdf;
import java.util.List;
-import java.util.ArrayList;
/**
* class representing a Root (/Catalog) object
@@ -63,11 +62,8 @@
/** The array of OutputIntents */
private List outputIntents;
- /**
- * The referencePDF value of the /Dests object,
- * if this PDF has a Name Dictionary
- */
- private String namesReferencePDF = null;
+ /** the /Dests object, if this PDF has a Names Dictionary */
+ private PDFNames names;
private int pageMode = PAGEMODE_USENONE;
@@ -133,12 +129,20 @@
}
/**
- * Set the optional Metadata object.
- * @param meta the Metadata object
- * @since PDF 1.4
+ * Set the Names object.
+ * @param names the Names object
+ * @since PDF 1.2
+ */
+ public void setNames(PDFNames names) {
+ this.names = names;
+ }
+
+ /**
+ * @return the Names object if set, null otherwise.
+ * @since PDF 1.2
*/
- public void setNames(String referencePDF) {
- this.namesReferencePDF = referencePDF;
+ public PDFNames getNames() {
+ return this.names;
}
/**
@@ -175,7 +179,7 @@
public String toPDFString() {
StringBuffer p = new StringBuffer(128);
p.append(getObjectID());
- p.append("<< /Type /Catalog\n/Pages "
+ p.append("<< /Type /Catalog\n /Pages "
+ this.rootPages.referencePDF()
+ "\n");
if (outline != null) {
@@ -197,17 +201,17 @@
break;
}
}
- if (getDocumentSafely().getHasDestinations() && namesReferencePDF != null) {
- p.append(" /Names " + namesReferencePDF + "\n");
+ 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");
+ p.append(" /Metadata " + getMetadata().referencePDF() + "\n");
}
if (this.outputIntents != null
&& this.outputIntents.size() > 0
&& getDocumentSafely().getPDFVersion() >= PDFDocument.PDF_VERSION_1_4) {
- p.append("/OutputIntents [");
+ p.append(" /OutputIntents [");
for (int i = 0, c = this.outputIntents.size(); i < c; i++) {
PDFOutputIntent outputIntent = (PDFOutputIntent)this.outputIntents.get(i);
if (i > 0) {
Modified: xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/pdf/PDFState.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/pdf/PDFState.java?rev=579125&r1=579124&r2=579125&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/pdf/PDFState.java (original)
+++ xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/pdf/PDFState.java Tue Sep 25 01:41:51 2007
@@ -301,7 +301,22 @@
}
/**
- * Get the grapics state.
+ * Get a copy of the base transform for the page. Used to translate
+ * IPP/BPP values into X,Y positions when positioning is "fixed".
+ *
+ * @return the base transform, or null if the state stack is empty
+ */
+ public AffineTransform getBaseTransform() {
+ if (stateStack.size() == 0) {
+ return null;
+ } else {
+ Data baseData = (Data) stateStack.get(0);
+ return (AffineTransform) baseData.transform.clone();
+ }
+ }
+
+ /**
+ * Get the graphics state.
* This gets the combination of all graphic states for
* the current context.
* This is the graphic state set with the gs operator not
Modified: xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/pdf/PDFStream.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/pdf/PDFStream.java?rev=579125&r1=579124&r2=579125&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/pdf/PDFStream.java (original)
+++ xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/pdf/PDFStream.java Tue Sep 25 01:41:51 2007
@@ -57,7 +57,7 @@
*/
public void add(String s) {
try {
- data.getOutputStream().write(s.getBytes());
+ data.getOutputStream().write(PDFDocument.encode(s));
} catch (IOException ex) {
//TODO throw the exception and catch it elsewhere
ex.printStackTrace();
Modified: xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/render/AbstractRenderer.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/render/AbstractRenderer.java?rev=579125&r1=579124&r2=579125&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/render/AbstractRenderer.java (original)
+++ xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/render/AbstractRenderer.java Tue Sep 25 01:41:51 2007
@@ -165,7 +165,7 @@
/**
* @see org.apache.fop.render.Renderer#processOffDocumentItem(OffDocumentItem)
*/
- public void processOffDocumentItem(OffDocumentItem oDI) { }
+ public void processOffDocumentItem(OffDocumentItem odi) { }
/** @see org.apache.fop.render.Renderer#getGraphics2DAdapter() */
public Graphics2DAdapter getGraphics2DAdapter() {
Modified: xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/render/Renderer.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/render/Renderer.java?rev=579125&r1=579124&r2=579125&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/render/Renderer.java (original)
+++ xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/render/Renderer.java Tue Sep 25 01:41:51 2007
@@ -113,9 +113,9 @@
* document (e.g., PDF bookmarks). Note - not all renderers will process
* all off-document items.
*
- * @param ext The extension element to be rendered
+ * @param odi The off-document item to be rendered
*/
- void processOffDocumentItem(OffDocumentItem ext);
+ void processOffDocumentItem(OffDocumentItem odi);
/**
* @return the adapter for painting Java2D images (or null if not supported)
Modified: xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/render/bitmap/PNGRenderer.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/render/bitmap/PNGRenderer.java?rev=579125&r1=579124&r2=579125&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/render/bitmap/PNGRenderer.java (original)
+++ xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/render/bitmap/PNGRenderer.java Tue Sep 25 01:41:51 2007
@@ -20,10 +20,6 @@
package org.apache.fop.render.bitmap;
import java.awt.image.RenderedImage;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
@@ -33,7 +29,6 @@
import org.apache.commons.io.IOUtils;
-import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.MimeConstants;
import org.apache.fop.area.PageViewport;
import org.apache.fop.render.java2d.Java2DRenderer;
@@ -51,14 +46,11 @@
/** The file extension expected for PNG files */
private static final String PNG_FILE_EXTENSION = "png";
- /** The file syntax prefix, eg. "page" will output "page1.png" etc */
- private String filePrefix;
-
- /** The output directory where images are to be written */
- private File outputDir;
-
/** The OutputStream for the first Image */
private OutputStream firstOutputStream;
+
+ /** Helper class for generating multiple files */
+ private MultiFileRenderingUtil multiFileUtil;
/** @see org.apache.fop.render.AbstractRenderer */
public String getMimeType() {
@@ -68,54 +60,11 @@
/** @see org.apache.fop.render.Renderer#startRenderer(java.io.OutputStream) */
public void startRenderer(OutputStream outputStream) throws IOException {
log.info("rendering areas to PNG");
- setOutputDirectory();
+ multiFileUtil = new MultiFileRenderingUtil(PNG_FILE_EXTENSION,
+ getUserAgent().getOutputFile());
this.firstOutputStream = outputStream;
}
- /**
- * Sets the output directory, either from the outfile specified on the
- * command line, or from the directory specified in configuration file.
- * Also sets the file name syntax, eg. "page".
- * The file name must not have an extension, or must have extension "png",
- * and its last period must not be at the start (empty file prefix).
- *
- * @throws IOException if an invalid output file name was specified
- * (e.g., with an extension other than '.png')
- */
- private void setOutputDirectory() throws IOException {
-
- // the file provided on the command line
- File f = getUserAgent().getOutputFile();
- if (f == null) {
- //No filename information available. Only the first page will be rendered.
- outputDir = null;
- filePrefix = null;
- } else {
- outputDir = f.getParentFile();
-
- // extracting file name syntax
- String s = f.getName();
- int i = s.lastIndexOf(".");
- if (i > 0) {
- // Make sure that the file extension was "png"
- String extension = s.substring(i + 1).toLowerCase();
- if (!PNG_FILE_EXTENSION.equals(extension)) {
- throw new IOException("Invalid file extension ('"
- + extension + "') specified");
- }
- } else if (i == -1) {
- i = s.length();
- } else { // i == 0
- throw new IOException("Invalid file name ('"
- + s + "') specified");
- }
- if (s.charAt(i - 1) == '1') {
- i--; // getting rid of the "1"
- }
- filePrefix = s.substring(0, i);
- }
- }
-
/** @see org.apache.fop.render.Renderer#stopRenderer() */
public void stopRenderer() throws IOException {
@@ -131,23 +80,14 @@
}
try {
// Do the rendering: get the image for this page
- RenderedImage image = (RenderedImage) getPageImage((PageViewport) pageViewportList
- .get(i));
+ PageViewport pv = (PageViewport)pageViewportList.get(i);
+ RenderedImage image = (RenderedImage)getPageImage(pv);
// Encode this image
- log.debug("Encoding page " + (i + 1));
- ImageWriterParams params = new ImageWriterParams();
- params.setResolution(Math.round(userAgent.getTargetResolution()));
-
- // Encode PNG image
- ImageWriter writer = ImageWriterRegistry.getInstance().getWriterFor(getMimeType());
- if (writer == null) {
- throw new IOException("Could not get an ImageWriter to produce "
- + getMimeType() + ". The most likely explanation for this is a class"
- + " loading problem.");
+ if (log.isDebugEnabled()) {
+ log.debug("Encoding page " + (i + 1));
}
- log.debug("Writing image using " + writer.getClass().getName());
- writer.writeImage(image, os, params);
+ writeImage(os, image);
} finally {
//Only close self-created OutputStreams
if (os != firstOutputStream) {
@@ -157,29 +97,36 @@
}
}
+ private void writeImage(OutputStream os, RenderedImage image) throws IOException {
+ ImageWriterParams params = new ImageWriterParams();
+ params.setResolution(Math.round(userAgent.getTargetResolution()));
+
+ // Encode PNG image
+ ImageWriter writer = ImageWriterRegistry.getInstance().getWriterFor(getMimeType());
+ if (writer == null) {
+ throw new IOException("Could not get an ImageWriter to produce "
+ + getMimeType() + ". The most likely explanation for this is a class"
+ + " loading problem.");
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("Writing image using " + writer.getClass().getName());
+ }
+ writer.writeImage(image, os, params);
+ }
+
/**
- * Builds the OutputStream corresponding to this page
- * @param 0-based pageNumber
+ * Returns the OutputStream corresponding to this page
+ * @param pageNumber 0-based page number
* @return the corresponding OutputStream
+ * @throws IOException In case of an I/O error
*/
- private OutputStream getCurrentOutputStream(int pageNumber) {
+ protected OutputStream getCurrentOutputStream(int pageNumber) throws IOException {
if (pageNumber == 0) {
return firstOutputStream;
- }
-
- if (filePrefix == null) {
- return null;
} else {
- File f = new File(outputDir,
- filePrefix + (pageNumber + 1) + "." + PNG_FILE_EXTENSION);
- try {
- OutputStream os = new BufferedOutputStream(new FileOutputStream(f));
- return os;
- } catch (FileNotFoundException e) {
- new FOPException("Can't build the OutputStream\n" + e);
- return null;
- }
+ return multiFileUtil.createOutputStream(pageNumber);
}
+
}
}
Modified: xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/render/pdf/PDFRenderer.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/render/pdf/PDFRenderer.java?rev=579125&r1=579124&r2=579125&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/render/pdf/PDFRenderer.java (original)
+++ xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/render/pdf/PDFRenderer.java Tue Sep 25 01:41:51 2007
@@ -24,6 +24,7 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
+import java.awt.geom.Point2D;
import java.awt.Color;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_Profile;
@@ -48,6 +49,8 @@
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.MimeConstants;
+import org.apache.fop.area.Area;
+import org.apache.fop.area.Block;
import org.apache.fop.area.CTM;
import org.apache.fop.area.LineArea;
import org.apache.fop.area.OffDocumentExtensionAttachment;
@@ -60,6 +63,7 @@
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.area.inline.Image;
import org.apache.fop.area.inline.Leader;
+import org.apache.fop.area.inline.InlineArea;
import org.apache.fop.area.inline.InlineParent;
import org.apache.fop.area.inline.WordArea;
import org.apache.fop.area.inline.SpaceArea;
@@ -69,17 +73,20 @@
import org.apache.fop.image.FopImage;
import org.apache.fop.image.ImageFactory;
import org.apache.fop.image.XMLImage;
+import org.apache.fop.pdf.PDFAction;
import org.apache.fop.pdf.PDFAMode;
import org.apache.fop.pdf.PDFAnnotList;
import org.apache.fop.pdf.PDFColor;
import org.apache.fop.pdf.PDFConformanceException;
-import org.apache.fop.pdf.PDFDestination;
import org.apache.fop.pdf.PDFDocument;
import org.apache.fop.pdf.PDFEncryptionManager;
import org.apache.fop.pdf.PDFEncryptionParams;
+import org.apache.fop.pdf.PDFFactory;
import org.apache.fop.pdf.PDFFilterList;
+import org.apache.fop.pdf.PDFGoTo;
import org.apache.fop.pdf.PDFICCBasedColorSpace;
import org.apache.fop.pdf.PDFICCStream;
+import org.apache.fop.pdf.PDFGoTo;
import org.apache.fop.pdf.PDFInfo;
import org.apache.fop.pdf.PDFLink;
import org.apache.fop.pdf.PDFMetadata;
@@ -164,16 +171,35 @@
protected Map pages = null;
/**
- * Page references are stored using the PageViewport as the key
- * when a reference is made the PageViewport is used
- * for pdf this means we need the pdf page reference
+ * Maps unique PageViewport key to PDF page reference
*/
protected Map pageReferences = new java.util.HashMap();
- /** Page viewport references */
+ /**
+ * Maps unique PageViewport key back to PageViewport itself
+ */
protected Map pvReferences = new java.util.HashMap();
/**
+ * Maps XSL-FO element IDs to their on-page XY-positions
+ * Must be used in conjunction with the page reference to fully specify the PDFGoTo details
+ */
+ protected Map idPositions = new java.util.HashMap();
+
+ /**
+ * Maps XSL-FO element IDs to PDFGoTo objects targeting the corresponding areas
+ * These objects may not all be fully filled in yet
+ */
+ protected Map idGoTos = new java.util.HashMap();
+
+ /**
+ * The PDFGoTos in idGoTos that are not complete yet
+ */
+ protected List unfinishedGoTos = new java.util.ArrayList();
+ // can't use a Set because PDFGoTo.equals returns true if the target is the same,
+ // even if the object number differs
+
+ /**
* The output stream to write the document to
*/
protected OutputStream ostream;
@@ -197,7 +223,12 @@
* the current page to add annotations to
*/
protected PDFPage currentPage;
-
+
+ /**
+ * the current page's PDF reference string (to avoid numerous function calls)
+ */
+ protected String currentPageRef;
+
/** the (optional) encryption parameters */
protected PDFEncryptionParams encryptionParams;
@@ -479,9 +510,34 @@
}
/**
+ * Checks if there are any unfinished PDFGoTos left in the list and resolves them
+ * to a default position on the page. Logs a warning, as this should not happen.
+ */
+ protected void finishOpenGoTos() {
+ int count = unfinishedGoTos.size();
+ if (count > 0) {
+ // TODO : page height may not be the same for all targeted pages
+ Point2D.Float defaultPos = new Point2D.Float(0f, pageHeight / 1000f); // top-o-page
+ while (!unfinishedGoTos.isEmpty()) {
+ PDFGoTo gt = (PDFGoTo) unfinishedGoTos.get(0);
+ finishIDGoTo(gt, defaultPos);
+ }
+ boolean one = count == 1;
+ String pl = one ? "" : "s";
+ String ww = one ? "was" : "were";
+ String ia = one ? "is" : "are";
+ log.warn("" + count + " link target" + pl + " could not be fully resolved and "
+ + ww + " now point to the top of the page or "
+ + ia + " dysfunctional."); // dysfunctional if pageref is null
+ }
+ }
+
+ /**
* @see org.apache.fop.render.Renderer#stopRenderer()
*/
public void stopRenderer() throws IOException {
+ finishOpenGoTos();
+
pdfDoc.getResources().addFonts(pdfDoc, fontInfo);
pdfDoc.outputTrailer(ostream);
@@ -498,6 +554,9 @@
currentPage = null;
currentState = null;
currentFontName = "";
+
+ idPositions.clear();
+ idGoTos.clear();
}
/**
@@ -512,19 +571,11 @@
* @see org.apache.fop.render.Renderer#processOffDocumentItem(OffDocumentItem)
*/
public void processOffDocumentItem(OffDocumentItem odi) {
- // render Destinations
if (odi instanceof DestinationData) {
- PDFDestination destination = pdfDoc.getFactory().makeDestination((DestinationData) odi);
- PageViewport pv = destination.getPageViewport();
- String dest = (String)pageReferences.get(pv.getKey());
- Rectangle2D bounds = pv.getViewArea();
- double h = bounds.getHeight();
- float yoffset = (float)h / 1000f;
- String gtRef = pdfDoc.getFactory().getGoToReference(dest, yoffset);
- destination.setGoToReference(gtRef);
- }
- // render Bookmark-Tree
- else if (odi instanceof BookmarkData) {
+ // render Destinations
+ renderDestination((DestinationData) odi);
+ } else if (odi instanceof BookmarkData) {
+ // render Bookmark-Tree
renderBookmarkTree((BookmarkData) odi);
} else if (odi instanceof OffDocumentExtensionAttachment) {
ExtensionAttachment attachment = ((OffDocumentExtensionAttachment)odi).getAttachment();
@@ -534,6 +585,21 @@
}
}
+ private void renderDestination(DestinationData dd) {
+ String targetID = dd.getIDRef();
+ if (targetID != null && targetID.length() > 0) {
+ PageViewport pv = dd.getPageViewport();
+ if (pv == null) {
+ log.warn("Unresolved destination item received: " + dd.getIDRef());
+ }
+ PDFGoTo gt = getPDFGoToForID(targetID, pv.getKey());
+ pdfDoc.getFactory().makeDestination(
+ dd.getIDRef(), gt.makeReference());
+ } else {
+ log.warn("DestinationData item with null or empty IDRef received.");
+ }
+ }
+
/**
* Renders a Bookmark-Tree object
* @param bookmarks the BookmarkData object containing all the Bookmark-Items
@@ -545,36 +611,34 @@
}
}
- private void renderBookmarkItem(BookmarkData bookmarkItem,
- PDFOutline parentBookmarkItem) {
+ private void renderBookmarkItem(BookmarkData bookmarkItem,
+ PDFOutline parentBookmarkItem) {
PDFOutline pdfOutline = null;
- PageViewport pv = bookmarkItem.getPageViewport();
- if (pv != null) {
- Rectangle2D bounds = pv.getViewArea();
- double h = bounds.getHeight();
- float yoffset = (float)h / 1000f;
- String intDest = (String)pageReferences.get(pv.getKey());
- if (parentBookmarkItem == null) {
- PDFOutline outlineRoot = pdfDoc.getOutlineRoot();
- pdfOutline = pdfDoc.getFactory().makeOutline(outlineRoot,
- bookmarkItem.getBookmarkTitle(),
- intDest, yoffset,
- bookmarkItem.showChildItems());
+
+ String targetID = bookmarkItem.getIDRef();
+ if (targetID != null && targetID.length() > 0) {
+ PageViewport pv = bookmarkItem.getPageViewport();
+ if (pv != null) {
+ String pvKey = pv.getKey();
+ PDFGoTo gt = getPDFGoToForID(targetID, pvKey);
+ // create outline object:
+ PDFOutline parent = parentBookmarkItem != null
+ ? parentBookmarkItem
+ : pdfDoc.getOutlineRoot();
+ pdfOutline = pdfDoc.getFactory().makeOutline(parent,
+ bookmarkItem.getBookmarkTitle(), gt, bookmarkItem.showChildItems());
} else {
- pdfOutline = pdfDoc.getFactory().makeOutline(parentBookmarkItem,
- bookmarkItem.getBookmarkTitle(),
- intDest, yoffset,
- bookmarkItem.showChildItems());
+ log.warn("Bookmark with IDRef \"" + targetID + "\" has a null PageViewport.");
}
} else {
- log.warn("Unresolved bookmark item received: " + bookmarkItem.getIDRef());
+ log.warn("Bookmark item with null or empty IDRef received.");
}
for (int i = 0; i < bookmarkItem.getCount(); i++) {
renderBookmarkItem(bookmarkItem.getSubData(i), pdfOutline);
}
}
-
+
private void renderXMPMetadata(XMPMetadata metadata) {
Metadata docXMP = metadata.getMetadata();
Metadata fopXMP = PDFMetadata.createXMPFromUserAgent(pdfDoc);
@@ -707,6 +771,8 @@
} else {
setupPage(page);
}
+ currentPageRef = currentPage.referencePDF();
+
Rectangle2D bounds = page.getViewArea();
double h = bounds.getHeight();
pageHeight = (int) h;
@@ -1089,6 +1155,188 @@
}
/**
+ * Returns area's id if it is the first area in the document with that id
+ * (i.e. if the area qualifies as a link target).
+ * Otherwise, or if the area has no id, null is returned.
+ *
+ * NOTE : area must be on currentPageViewport, otherwise result may be wrong!
+ *
+ * @param area the area for which to return the id
+ */
+ protected String getTargetableID(Area area) {
+ String id = (String) area.getTrait(Trait.PROD_ID);
+ if (id == null || id.length() == 0
+ || !currentPageViewport.isFirstWithID(id)
+ || idPositions.containsKey(id)) {
+ return null;
+ } else {
+ return id;
+ }
+ }
+
+ /**
+ * Set XY position in the PDFGoTo and add it to the PDF trailer.
+ *
+ * @param gt the PDFGoTo object
+ * @param position the X,Y position to set
+ */
+ protected void finishIDGoTo(PDFGoTo gt, Point2D.Float position) {
+ gt.setPosition(position);
+ pdfDoc.addTrailerObject(gt);
+ unfinishedGoTos.remove(gt);
+ }
+
+ /**
+ * Set page reference and XY position in the PDFGoTo and add it to the PDF trailer.
+ *
+ * @param gt the PDFGoTo object
+ * @param pdfPageRef the PDF reference string of the target page object
+ * @param position the X,Y position to set
+ */
+ protected void finishIDGoTo(PDFGoTo gt, String pdfPageRef, Point2D.Float position) {
+ gt.setPageReference(pdfPageRef);
+ finishIDGoTo(gt, position);
+ }
+
+ /**
+ * Get a PDFGoTo pointing to the given id. Create one if necessary.
+ * It is possible that the PDFGoTo is not fully resolved yet. In that case
+ * it must be completed (and added to the PDF trailer) later.
+ *
+ * @param targetID the target id of the PDFGoTo
+ * @param pvKey the unique key of the target PageViewport
+ *
+ * @return the PDFGoTo that was found or created
+ */
+ protected PDFGoTo getPDFGoToForID(String targetID, String pvKey) {
+ // Already a PDFGoTo present for this target? If not, create.
+ PDFGoTo gt = (PDFGoTo) idGoTos.get(targetID);
+ if (gt == null) {
+ String pdfPageRef = (String) pageReferences.get(pvKey);
+ Point2D.Float position = (Point2D.Float) idPositions.get(targetID);
+ // can the GoTo already be fully filled in?
+ if (pdfPageRef != null && position != null) {
+ // getPDFGoTo shares PDFGoTo objects as much as possible.
+ // It also takes care of assignObjectNumber and addTrailerObject.
+ gt = pdfDoc.getFactory().getPDFGoTo(pdfPageRef, position);
+ } else {
+ // Not complete yet, can't use getPDFGoTo:
+ gt = new PDFGoTo(pdfPageRef);
+ pdfDoc.assignObjectNumber(gt);
+ // pdfDoc.addTrailerObject() will be called later, from finishIDGoTo()
+ unfinishedGoTos.add(gt);
+ }
+ idGoTos.put(targetID, gt);
+ }
+ return gt;
+ }
+
+ /**
+ * Saves id's absolute position on page for later retrieval by PDFGoTos
+ *
+ * @param id the id of the area whose position must be saved
+ * @param pdfPageRef the PDF page reference string
+ * @param relativeIPP the *relative* IP position in millipoints
+ * @param relativeBPP the *relative* BP position in millipoints
+ * @param tf the transformation to apply once the relative positions have been
+ * converted to points
+ */
+ protected void saveAbsolutePosition(String id, String pdfPageRef,
+ int relativeIPP, int relativeBPP, AffineTransform tf) {
+ Point2D.Float position = new Point2D.Float(relativeIPP / 1000f, relativeBPP / 1000f);
+ tf.transform(position, position);
+ idPositions.put(id, position);
+ // is there already a PDFGoTo waiting to be completed?
+ PDFGoTo gt = (PDFGoTo) idGoTos.get(id);
+ if (gt != null) {
+ finishIDGoTo(gt, pdfPageRef, position);
+ }
+/*
+ // The code below auto-creates a named destination for every id in the document.
+ // This should probably be controlled by a user-configurable setting, as it may
+ // make the PDF file grow noticeably.
+ // *** NOT YET WELL-TESTED ! ***
+ if (true) {
+ PDFFactory factory = pdfDoc.getFactory();
+ if (gt == null) {
+ gt = factory.getPDFGoTo(pdfPageRef, position);
+ idGoTos.put(id, gt); // so others can pick it up too
+ }
+ factory.makeDestination(id, gt.referencePDF(), currentPageViewport);
+ // Note: using currentPageViewport is only correct if the id is indeed on
+ // the current PageViewport. But even if incorrect, it won't interfere with
+ // what gets created in the PDF.
+ // For speedup, we should also create a lookup map id -> PDFDestination
+ }
+*/
+ }
+
+ /**
+ * Saves id's absolute position on page for later retrieval by PDFGoTos,
+ * using the currently valid transformation and the currently valid PDF page reference
+ *
+ * @param id the id of the area whose position must be saved
+ * @param relativeIPP the *relative* IP position in millipoints
+ * @param relativeBPP the *relative* BP position in millipoints
+ */
+ protected void saveAbsolutePosition(String id, int relativeIPP, int relativeBPP) {
+ saveAbsolutePosition(id, currentPageRef,
+ relativeIPP, relativeBPP, currentState.getTransform());
+ }
+
+ /**
+ * If the given block area is a possible link target, its id + absolute position will
+ * be saved. The saved position is only correct if this function is called at the very
+ * start of renderBlock!
+ *
+ * @param block the block area in question
+ */
+ protected void saveBlockPosIfTargetable(Block block) {
+ String id = getTargetableID(block);
+ if (id != null) {
+ // FIXME: Like elsewhere in the renderer code, absolute and relative
+ // directions are happily mixed here. This makes sure that the
+ // links point to the right location, but it is not correct.
+ int ipp = block.getXOffset();
+ int bpp = block.getYOffset() + block.getSpaceBefore();
+ int positioning = block.getPositioning();
+ if (!(positioning == Block.FIXED || positioning == Block.ABSOLUTE)) {
+ ipp += currentIPPosition;
+ bpp += currentBPPosition;
+ }
+ AffineTransform tf = positioning == Block.FIXED
+ ? currentState.getBaseTransform()
+ : currentState.getTransform();
+ saveAbsolutePosition(id, currentPageRef, ipp, bpp, tf);
+ }
+ }
+
+ /**
+ * If the given inline area is a possible link target, its id + absolute position will
+ * be saved. The saved position is only correct if this function is called at the very
+ * start of renderInlineArea!
+ *
+ * @param inlineArea the inline area in question
+ */
+ protected void saveInlinePosIfTargetable(InlineArea inlineArea) {
+ String id = getTargetableID(inlineArea);
+ if (id != null) {
+ int extraMarginBefore = 5000; // millipoints
+ int ipp = currentIPPosition;
+ int bpp = currentBPPosition + inlineArea.getOffset() - extraMarginBefore;
+ saveAbsolutePosition(id, ipp, bpp);
+ }
+ }
+
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#renderBlock(Block)
+ */
+ protected void renderBlock(Block block) {
+ saveBlockPosIfTargetable(block);
+ super.renderBlock(block);
+ }
+
+ /**
* @see org.apache.fop.render.AbstractRenderer#renderLineArea(LineArea)
*/
protected void renderLineArea(LineArea line) {
@@ -1097,53 +1345,88 @@
}
/**
+ * @see org.apache.fop.render.AbstractRenderer#renderInlineArea(InlineArea)
+ */
+ protected void renderInlineArea(InlineArea inlineArea) {
+ saveInlinePosIfTargetable(inlineArea);
+ super.renderInlineArea(inlineArea);
+ }
+
+ /**
* Render inline parent area.
* For pdf this handles the inline parent area traits such as
* links, border, background.
* @param ip the inline parent area
*/
public void renderInlineParent(InlineParent ip) {
- float start = currentIPPosition / 1000f;
- float top = (ip.getOffset() + currentBPPosition) / 1000f;
- float width = ip.getIPD() / 1000f;
- float height = ip.getBPD() / 1000f;
+ boolean annotsAllowed = pdfDoc.getProfile().isAnnotationAllowed();
+
+ // stuff we only need if a link must be created:
+ Rectangle2D ipRect = null;
+ PDFFactory factory = null;
+ PDFAction action = null;
+ if (annotsAllowed) {
+ // make sure the rect is determined *before* calling super!
+ int ipp = currentIPPosition;
+ int bpp = currentBPPosition + ip.getOffset();
+ ipRect = new Rectangle2D.Float(ipp / 1000f, bpp / 1000f,
+ ip.getIPD() / 1000f, ip.getBPD() / 1000f);
+ AffineTransform transform = currentState.getTransform();
+ ipRect = transform.createTransformedShape(ipRect).getBounds2D();
+
+ factory = pdfDoc.getFactory();
+ }
+
// render contents
super.renderInlineParent(ip);
- if (pdfDoc.getProfile().isAnnotationAllowed()) {
- // place the link over the top
- Object tr = ip.getTrait(Trait.INTERNAL_LINK);
- boolean internal = false;
- String dest = null;
- float yoffset = 0;
- if (tr == null) {
- dest = (String)ip.getTrait(Trait.EXTERNAL_LINK);
+ boolean linkTraitFound = false;
+
+ // try INTERNAL_LINK first
+ Trait.InternalLink intLink = (Trait.InternalLink) ip.getTrait(Trait.INTERNAL_LINK);
+ if (intLink != null) {
+ linkTraitFound = true;
+ String pvKey = intLink.getPVKey();
+ String idRef = intLink.getIDRef();
+ boolean pvKeyOK = pvKey != null && pvKey.length() > 0;
+ boolean idRefOK = idRef != null && idRef.length() > 0;
+ if (pvKeyOK && idRefOK) {
+ if (annotsAllowed) {
+ action = getPDFGoToForID(idRef, pvKey);
+ }
+ } else if (pvKeyOK) {
+ log.warn("Internal link trait with PageViewport key " + pvKey
+ + " contains no ID reference.");
+ } else if (idRefOK) {
+ log.warn("Internal link trait with ID reference " + idRef
+ + " contains no PageViewport key.");
} else {
- String pvKey = (String)tr;
- dest = (String)pageReferences.get(pvKey);
- if (dest != null) {
- PageViewport pv = (PageViewport)pvReferences.get(pvKey);
- Rectangle2D bounds = pv.getViewArea();
- double h = bounds.getHeight();
- yoffset = (float)h / 1000f;
- internal = true;
+ log.warn("Internal link trait received with neither PageViewport key"
+ + " nor ID reference.");
+ }
+ }
+
+ // no INTERNAL_LINK, look for EXTERNAL_LINK
+ if (!linkTraitFound) {
+ String extDest = (String) ip.getTrait(Trait.EXTERNAL_LINK);
+ if (extDest != null && extDest.length() > 0) {
+ linkTraitFound = true;
+ if (annotsAllowed) {
+ action = factory.getExternalAction(extDest);
}
}
- if (dest != null) {
- // add link to pdf document
- Rectangle2D rect = new Rectangle2D.Float(start, top, width, height);
- // transform rect to absolute coords
- AffineTransform transform = currentState.getTransform();
- rect = transform.createTransformedShape(rect).getBounds2D();
-
- int type = internal ? PDFLink.INTERNAL : PDFLink.EXTERNAL;
- PDFLink pdflink = pdfDoc.getFactory().makeLink(
- rect, dest, type, yoffset);
- currentPage.addAnnotation(pdflink);
+ }
+
+ // warn if link trait found but not allowed, else create link
+ if (linkTraitFound) {
+ if (!annotsAllowed) {
+ log.warn("Skipping annotation for a link due to PDF profile: "
+ + pdfDoc.getProfile());
+ } else if (action != null) {
+ PDFLink pdfLink = factory.makeLink(ipRect, action);
+ currentPage.addAnnotation(pdfLink);
}
- } else {
- log.warn("Skipping annotation for a link due to PDF profile: " + pdfDoc.getProfile());
}
}
@@ -1199,12 +1482,12 @@
String s = word.getWord();
escapeText(s, word.getLetterAdjustArray(),
font, (AbstractTextArea)word.getParentArea(), useMultiByte, pdf);
-
+
currentStream.add(pdf.toString());
super.renderWord(word);
}
-
+
/**
* @see org.apache.fop.render.AbstractRenderer#renderSpace(SpaceArea)
*/
@@ -1223,18 +1506,18 @@
if (space.isAdjustable()) {
int tws = -((TextArea) space.getParentArea()).getTextWordSpaceAdjust()
- 2 * textArea.getTextLetterSpaceAdjust();
-
+
if (tws != 0) {
pdf.append(format(tws / (font.getFontSize() / 1000f)));
pdf.append(" ");
}
}
-
+
currentStream.add(pdf.toString());
super.renderSpace(space);
}
-
+
/**
* Escapes text according to PDF rules.
* @param s Text to escape
@@ -1282,9 +1565,9 @@
}
}
if (letterAdjust != null && i < l - 1) {
- glyphAdjust -= letterAdjust[i + 1];
+ glyphAdjust -= letterAdjust[i + 1];
}
-
+
if (startPending) {
pdf.append(startText);
startPending = false;
@@ -1309,12 +1592,12 @@
}
float adjust = glyphAdjust / fontSize;
-
+
if (adjust != 0) {
pdf.append(endText).append(format(adjust)).append(' ');
startPending = true;
}
-
+
}
if (!startPending) {
pdf.append(endText);
Modified: xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/render/xml/XMLRenderer.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/render/xml/XMLRenderer.java?rev=579125&r1=579124&r2=579125&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/render/xml/XMLRenderer.java (original)
+++ xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/java/org/apache/fop/render/xml/XMLRenderer.java Tue Sep 25 01:41:51 2007
@@ -60,11 +60,13 @@
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.inline.Container;
import org.apache.fop.area.inline.ForeignObject;
@@ -350,6 +352,9 @@
addAttribute("font-name", triplet.getName());
addAttribute("font-style", triplet.getStyle());
addAttribute("font-weight", triplet.getWeight());
+ } else if (clazz.equals(InternalLink.class)) {
+ InternalLink iLink = (InternalLink)value;
+ addAttribute(name, iLink.xmlAttribute());
} else if (clazz.equals(Background.class)) {
Background bkg = (Background)value;
//TODO Remove the following line (makes changes in the test checks necessary)
@@ -431,7 +436,9 @@
/** @see org.apache.fop.render.AbstractRenderer#processOffDocumentItem(OffDocumentItem) */
public void processOffDocumentItem(OffDocumentItem oDI) {
- if (oDI instanceof OffDocumentExtensionAttachment) {
+ if (oDI instanceof BookmarkData) {
+ renderBookmarkTree((BookmarkData) oDI);
+ } else if (oDI instanceof OffDocumentExtensionAttachment) {
ExtensionAttachment attachment = ((OffDocumentExtensionAttachment)oDI).getAttachment();
if (extensionAttachments == null) {
extensionAttachments = new java.util.ArrayList();
@@ -444,6 +451,40 @@
}
/**
+ * Renders a BookmarkTree object
+ * @param bookmarkRoot the BookmarkData object representing the top of the tree
+ */
+ protected void renderBookmarkTree(BookmarkData bookmarkRoot) {
+ if (bookmarkRoot.getWhenToProcess() == OffDocumentItem.END_OF_DOC) {
+ endPageSequence();
+ }
+ /* If this kind of handling is also necessary for other renderers, then
+ better add endPageSequence to the Renderer interface and call it
+ explicitly from model.endDocument() */
+
+ startElement("bookmarkTree");
+ for (int i = 0; i < bookmarkRoot.getCount(); i++) {
+ renderBookmarkItem(bookmarkRoot.getSubData(i));
+ }
+ endElement("bookmarkTree");
+ }
+
+ private void renderBookmarkItem(BookmarkData bm) {
+ atts.clear();
+ addAttribute("title", bm.getBookmarkTitle());
+ addAttribute("show-children", String.valueOf(bm.showChildItems()));
+ PageViewport pv = bm.getPageViewport();
+ String pvKey = pv == null ? null : pv.getKey();
+ addAttribute("internal-link",
+ InternalLink.makeXMLAttribute(pvKey, bm.getIDRef()));
+ startElement("bookmark", atts);
+ for (int i = 0; i < bm.getCount(); i++) {
+ renderBookmarkItem(bm.getSubData(i));
+ }
+ endElement("bookmark");
+ }
+
+ /**
* @see org.apache.fop.render.Renderer#startRenderer(OutputStream)
*/
public void startRenderer(OutputStream outputStream)
@@ -480,9 +521,7 @@
* @see org.apache.fop.render.Renderer#stopRenderer()
*/
public void stopRenderer() throws IOException {
- if (startedSequence) {
- endElement("pageSequence");
- }
+ endPageSequence();
endElement("areaTree");
try {
handler.endDocument();
@@ -549,9 +588,7 @@
*/
public void startPageSequence(LineArea seqTitle) {
handleDocumentExtensionAttachments();
- if (startedSequence) {
- endElement("pageSequence");
- }
+ endPageSequence(); // move this before handleDocumentExtensionAttachments() ?
startedSequence = true;
startElement("pageSequence");
if (seqTitle != null) {
@@ -568,6 +605,16 @@
}
/**
+ * Tells the renderer to finish the current PageSequence
+ */
+ public void endPageSequence() {
+ if (startedSequence) {
+ endElement("pageSequence");
+ }
+ startedSequence = false;
+ }
+
+ /**
* @see org.apache.fop.render.AbstractRenderer#renderRegionViewport(RegionViewport)
*/
protected void renderRegionViewport(RegionViewport port) {
@@ -756,6 +803,24 @@
startElement("lineArea", atts);
super.renderLineArea(line);
endElement("lineArea");
+ }
+
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#renderInlineArea(InlineArea)
+ */
+ protected void renderInlineArea(InlineArea inlineArea) {
+ atts.clear();
+ if (inlineArea.getClass() == InlineArea.class) {
+ // Generic inline area. This is implemented to allow the 0x0 "dummy"
+ // area generated by fo:wrapper to pass its id.
+ addAreaAttributes(inlineArea);
+ addTraitAttributes(inlineArea);
+ startElement("inline", atts);
+ endElement("inline");
+ } else {
+ super.renderInlineArea(inlineArea);
+ // calls specific renderers for Text, Space, Viewport, etc. etc.
+ }
}
/**
Modified: xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/sandbox/org/apache/fop/render/svg/SVGRenderer.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/sandbox/org/apache/fop/render/svg/SVGRenderer.java?rev=579125&r1=579124&r2=579125&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/sandbox/org/apache/fop/render/svg/SVGRenderer.java (original)
+++ xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/sandbox/org/apache/fop/render/svg/SVGRenderer.java Tue Sep 25 01:41:51 2007
@@ -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.
@@ -19,425 +19,163 @@
package org.apache.fop.render.svg;
-import org.apache.fop.apps.FOPException;
-import org.apache.fop.area.CTM;
-import org.apache.fop.area.PageViewport;
-import org.apache.fop.area.LineArea;
-import org.apache.fop.area.inline.ForeignObject;
-import org.apache.fop.area.inline.InlineArea;
-import org.apache.fop.area.inline.Leader;
-import org.apache.fop.area.inline.TextArea;
-import org.apache.fop.svg.SVGUtilities;
-import org.apache.fop.fonts.FontInfo;
-import org.apache.fop.apps.FOUserAgent;
-
-/* org.w3c.dom.Document is not imported to avoid conflict with
- org.apache.fop.control.Document */
-import org.w3c.dom.DOMImplementation;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Text;
-import org.apache.batik.dom.svg.SVGDOMImplementation;
-import org.apache.batik.transcoder.svg2svg.SVGTranscoder;
-import org.apache.batik.transcoder.TranscoderInput;
-import org.apache.batik.transcoder.TranscoderOutput;
-import org.apache.batik.transcoder.TranscoderException;
-
-import java.awt.Color;
-import java.awt.image.BufferedImage;
+import java.awt.Dimension;
+import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
-import java.util.HashMap;
-import java.io.OutputStream;
import java.io.IOException;
-import java.io.OutputStreamWriter;
+import java.io.OutputStream;
+import java.io.Writer;
-import org.apache.fop.render.AbstractRenderer;
-import org.apache.fop.render.XMLHandler;
-import org.apache.fop.render.RendererContext;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.batik.dom.GenericDOMImplementation;
+import org.apache.batik.svggen.SVGGeneratorContext;
+import org.apache.batik.svggen.SVGGraphics2D;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fop.apps.MimeConstants;
+import org.apache.fop.area.PageViewport;
+import org.apache.fop.render.bitmap.MultiFileRenderingUtil;
+import org.apache.fop.render.java2d.Java2DGraphicsState;
+import org.apache.fop.render.java2d.Java2DRenderer;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Document;
/**
- * This is the SVG renderer.
+ * <p>
+ * This renderer generates SVG (Scalable Vector Graphics) format.
+ * <p>
+ * This class actually does not render itself, instead it extends
+ * <code>org.apache.fop.render.java2D.Java2DRenderer</code> and uses
+ * Apache Batik's SVGGraphics2D for SVG generation.
*/
-public class SVGRenderer extends AbstractRenderer {
-
- /** SVG MIME type */
- public static final String SVG_MIME_TYPE = "image/svg+xml";
-
- /** SVG namespace */
- public static final String SVG_NAMESPACE = SVGDOMImplementation.SVG_NAMESPACE_URI;
-
- private org.w3c.dom.Document svgDocument;
- private Element svgRoot;
- private Element currentPageG = null;
- private Element lastLink = null;
- private String lastViewbox = null;
+public class SVGRenderer extends Java2DRenderer {
- private Element docDefs = null;
- private Element pageDefs = null;
- private Element pagesGroup = null;
+ /** logging instance */
+ private static Log log = LogFactory.getLog(SVGRenderer.class);
- // first sequence title
- private LineArea docTitle = null;
+ /** The MIME type for the SVG format */
+ public static final String MIME_TYPE = MimeConstants.MIME_SVG;
- private OutputStream ostream;
+ private static final String SVG_FILE_EXTENSION = "svg";
- private float totalWidth = 0;
- private float totalHeight = 0;
- private float sequenceWidth = 0;
- private float sequenceHeight = 0;
+ private OutputStream firstOutputStream;
+
+ private Document document;
+
+ private SVGGraphics2D svgGenerator;
- private float pageWidth = 0;
- private float pageHeight = 0;
- private int pageNumber = 0;
-
- private HashMap fontNames = new HashMap();
- private HashMap fontStyles = new HashMap();
- private Color saveColor = null;
-
- /**
- * The current (internal) font name
- */
- private String currentFontName;
-
- /**
- * The current font size in millipoints
- */
- private int currentFontSize;
-
- /**
- * The current colour's red, green and blue component
- */
- private float currentRed = 0;
- private float currentGreen = 0;
- private float currentBlue = 0;
+ /** Helper class for generating multiple files */
+ private MultiFileRenderingUtil multiFileUtil;
+
+ /** @see org.apache.fop.render.AbstractRenderer */
+ public String getMimeType() {
+ return MIME_TYPE;
+ }
- /**
- * Creates a new SVG renderer.
- */
+ /** Creates TIFF renderer. */
public SVGRenderer() {
}
/**
- * @see org.apache.fop.render.Renderer#setUserAgent(FOUserAgent)
+ * @see org.apache.fop.render.AbstractRenderer#configure(
+ * org.apache.avalon.framework.configuration.Configuration)
*/
- public void setUserAgent(FOUserAgent agent) {
- super.setUserAgent(agent);
-
- //Note: This is done here as having two service lookup files in the same IDE project
- //will end up with one overwriting the other when all sources are compiled in to the
- //same target directory. Remove this code and add an entry in the XMLHandler resource
- //file when this renderer exits the sandbox.
- XMLHandler handler = agent.getXMLHandlerRegistry().getXMLHandler(this, SVG_NAMESPACE);
- if (handler == null) {
- agent.getXMLHandlerRegistry().addXMLHandler("org.apache.fop.render.svg.SVGSVGHandler");
- }
+ public void configure(Configuration cfg) throws ConfigurationException {
}
- /**
- * @see org.apache.fop.render.Renderer#setupFontInfo(FontInfo)
- */
- public void setupFontInfo(FontInfo fontInfo) {
- // create a temp Image to test font metrics on
- BufferedImage fontImage =
- new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
- org.apache.fop.render.java2d.FontSetup.setup(fontInfo,
- fontImage.createGraphics());
+ /** @see org.apache.fop.render.Renderer#startRenderer(java.io.OutputStream) */
+ public void startRenderer(OutputStream outputStream) throws IOException {
+ this.firstOutputStream = outputStream;
+ this.multiFileUtil = new MultiFileRenderingUtil(SVG_FILE_EXTENSION,
+ getUserAgent().getOutputFile());
+ super.startRenderer(this.firstOutputStream);
}
/**
- * @see org.apache.fop.render.Renderer#startRenderer(OutputStream)
+ * @see org.apache.fop.render.java2d.Java2DRenderer#renderPage(org.apache.fop.area.PageViewport)
*/
- public void startRenderer(OutputStream outputStream)
- throws IOException {
- ostream = outputStream;
- DOMImplementation impl = SVGDOMImplementation.getDOMImplementation();
- svgDocument = impl.createDocument(SVG_NAMESPACE, "svg", null);
- svgRoot = svgDocument.getDocumentElement();
- /*
- ProcessingInstruction pi =
- svgDocument.createProcessingInstruction("xml",
- " version=\"1.0\" encoding=\"ISO-8859-1\"");
- svgDocument.insertBefore(pi, svgRoot);
- */
-
- docDefs = svgDocument.createElementNS(SVG_NAMESPACE, "defs");
- svgRoot.appendChild(docDefs);
-
- pagesGroup = svgDocument.createElementNS(SVG_NAMESPACE, "g");
- pageDefs = svgDocument.createElementNS(SVG_NAMESPACE, "defs");
- pagesGroup.appendChild(pageDefs);
- svgRoot.appendChild(pagesGroup);
+ public void renderPage(PageViewport pageViewport) throws IOException {
+ log.debug("Rendering page: " + pageViewport.getPageNumberString());
+ // Get a DOMImplementation
+ DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
- }
+ // Create an instance of org.w3c.dom.Document
+ this.document = domImpl.createDocument(null, "svg", null);
- /**
- * @see org.apache.fop.render.Renderer#stopRenderer()
- */
- public void stopRenderer() throws IOException {
- totalWidth += sequenceWidth;
- if (sequenceHeight > totalHeight) {
- totalHeight = sequenceHeight;
- }
+ // Create an SVGGeneratorContext to customize SVG generation
+ SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(this.document);
+ ctx.setComment("Generated by " + userAgent.getProducer() + " with Batik SVG Generator");
+ ctx.setEmbeddedFontsOn(true);
+
+ // Create an instance of the SVG Generator
+ this.svgGenerator = new SVGGraphics2D(ctx, true);
+ Rectangle2D viewArea = pageViewport.getViewArea();
+ Dimension dim = new Dimension();
+ dim.setSize(viewArea.getWidth() / 1000, viewArea.getHeight() / 1000);
+ this.svgGenerator.setSVGCanvasSize(dim);
- svgRoot.setAttributeNS(null, "width", "" + (totalWidth + 1));
- svgRoot.setAttributeNS(null, "height", "" + (totalHeight + 1));
- //svgRoot.setAttributeNS(null, "viewBox", "0 0 " + pageWidth + " " + pageHeight);
- SVGTranscoder svgT = new SVGTranscoder();
- TranscoderInput input = new TranscoderInput(svgDocument);
- TranscoderOutput output =
- new TranscoderOutput(new OutputStreamWriter(ostream));
+ AffineTransform at = this.svgGenerator.getTransform();
+ this.state = new Java2DGraphicsState(this.svgGenerator, this.fontInfo, at);
try {
- svgT.transcode(input, output);
- } catch (TranscoderException e) {
- log.error("could not write svg file :" + e.getMessage(), e);
- }
- ostream.flush();
- ostream = null;
-
- svgDocument = null;
- svgRoot = null;
- currentPageG = null;
- lastLink = null;
-
- totalWidth = 0;
- totalHeight = 0;
-
- pageNumber = 0;
- }
-
- /**
- * @see org.apache.fop.render.Renderer#startPageSequence(LineArea)
- */
- public void startPageSequence(LineArea seqTitle) {
- totalWidth += sequenceWidth;
- if (sequenceHeight > totalHeight) {
- totalHeight = sequenceHeight;
- }
- sequenceWidth = 0;
- sequenceHeight = 0;
- if (seqTitle != null && docTitle == null) {
- // convert first title to a string and set for svg document title
- docTitle = seqTitle;
- String str = convertTitleToString(seqTitle);
- Element svgTitle = svgDocument.createElementNS(SVG_NAMESPACE, "title");
- Text strNode = svgDocument.createTextNode(str);
- svgTitle.appendChild(strNode);
- svgRoot.insertBefore(svgTitle, svgRoot.getFirstChild());
+ //super.renderPage(pageViewport);
+ renderPageAreas(pageViewport.getPage());
+ } finally {
+ this.state = null;
}
+ writeSVGFile(pageViewport.getPageIndex());
+
+ this.svgGenerator = null;
+ this.document = null;
+
}
- /**
- * @see org.apache.fop.render.Renderer#renderPage(PageViewport)
- */
- public void renderPage(PageViewport page) throws IOException, FOPException {
- float lastWidth = pageWidth;
- float lastHeight = pageHeight;
-
- Rectangle2D area = page.getViewArea();
- pageWidth = (float) area.getWidth() / 1000f;
- pageHeight = (float) area.getHeight() / 1000f;
-
- // if there is a link from the last page
- if (lastLink != null) {
- lastLink.setAttributeNS(null, "xlink:href", "#svgView(viewBox("
- + totalWidth + ", "
- + sequenceHeight + ", "
- + pageWidth + ", "
- + pageHeight + "))");
- pagesGroup.appendChild(lastLink);
- }
-
- currentPageG = svgDocument.createElementNS(SVG_NAMESPACE, "svg");
- currentPageG.setAttributeNS(null, "viewbox",
- "0 0 " + (int) pageWidth + " " + (int) pageHeight);
- currentPageG.setAttributeNS(null, "width",
- "" + ((int) pageWidth + 1));
- currentPageG.setAttributeNS(null, "height",
- "" + ((int) pageHeight + 1));
- currentPageG.setAttributeNS(null, "id", "Page-" + pageNumber);
- currentPageG.setAttributeNS(null, "style", "font-family:sanserif;font-size:12");
- pageDefs.appendChild(currentPageG);
+ /** @see org.apache.fop.render.Renderer#stopRenderer() */
+ public void stopRenderer() throws IOException {
+ super.stopRenderer();
- if (pageWidth > sequenceWidth) {
- sequenceWidth = pageWidth;
+ // Cleaning
+ clearViewportList();
+ log.debug("SVG generation complete.");
+ }
+
+ private void writeSVGFile(int pageNumber) throws IOException {
+ log.debug("Writing out SVG file...");
+ // Finally, stream out SVG to the standard output using UTF-8
+ // character to byte encoding
+ boolean useCSS = true; // we want to use CSS style attribute
+ OutputStream out = getCurrentOutputStream(pageNumber);
+ if (out == null) {
+ log.warn("No filename information available."
+ + " Stopping early after the first page.");
+ return;
}
- sequenceHeight += pageHeight;
-
- Element border =
- SVGUtilities.createRect(svgDocument, 0, 0, pageWidth,
- pageHeight);
- border.setAttributeNS(null, "style", "fill:none;stroke:black");
- currentPageG.appendChild(border);
-
- // render the page contents
- super.renderPage(page);
-
- Element use = svgDocument.createElementNS(SVG_NAMESPACE, "use");
- use.setAttributeNS(null, "xlink:href", "#Page-" + pageNumber);
- use.setAttributeNS(null, "x", "" + totalWidth);
- use.setAttributeNS(null, "y", "" + (sequenceHeight - pageHeight));
- pagesGroup.appendChild(use);
-
- Element lastPageLink = svgDocument.createElementNS(SVG_NAMESPACE, "a");
- if (lastLink != null) {
- lastPageLink.setAttributeNS(null, "xlink:href", lastViewbox);
- } else {
- lastPageLink.setAttributeNS(null, "xlink:href",
- "#svgView(viewBox("
- + totalWidth + ", "
- + (sequenceHeight - pageHeight) + ", "
- + pageWidth + ", "
- + pageHeight + "))");
+ try {
+ Writer writer = new java.io.OutputStreamWriter(out, "UTF-8");
+ this.svgGenerator.stream(writer, useCSS);
+ } finally {
+ if (out != this.firstOutputStream) {
+ IOUtils.closeQuietly(out);
+ } else {
+ out.flush();
+ }
}
- pagesGroup.appendChild(lastPageLink);
-
- // setup a link to the next page, only added when the
- // next page is rendered
- Element rect = SVGUtilities.createRect(svgDocument, totalWidth,
- (sequenceHeight - pageHeight), pageWidth / 2, pageHeight);
- rect.setAttributeNS(null, "style", "fill:blue;visibility:hidden");
- lastPageLink.appendChild(rect);
-
- lastLink = svgDocument.createElementNS(SVG_NAMESPACE, "a");
- rect = SVGUtilities.createRect(svgDocument,
- totalWidth + pageWidth / 2,
- (sequenceHeight - pageHeight), pageWidth / 2, pageHeight);
- rect.setAttributeNS(null, "style", "fill:blue;visibility:hidden");
- lastLink.appendChild(rect);
-
- lastViewbox = "#svgView(viewBox("
- + totalWidth + ", "
- + (sequenceHeight - pageHeight) + ", "
- + pageWidth + ", "
- + pageHeight + "))";
-
- pageNumber++;
-
- }
-
- /**
- * Method renderForeignObject.
- * @param fo the foreign object
- */
- public void renderForeignObject(ForeignObject fo, Rectangle2D pos) {
- org.w3c.dom.Document doc = fo.getDocument();
- String ns = fo.getNameSpace();
- renderDocument(doc, ns, pos);
- }
-
- /**
- * Renders an XML document (SVG for example).
- *
- * @param doc DOM document representing the XML document
- * @param ns Namespace for the document
- * @param pos Position on the page
- */
- public void renderDocument(Document doc, String ns, Rectangle2D pos) {
- RendererContext context;
- context = new RendererContext(this, getMimeType());
- context.setUserAgent(userAgent);
-
- context.setProperty(SVGRendererContextConstants.SVG_DOCUMENT, svgDocument);
- context.setProperty(SVGRendererContextConstants.SVG_PAGE_G, currentPageG);
- context.setProperty(SVGRendererContextConstants.XPOS,
- new Integer(currentIPPosition + (int)pos.getX()));
- context.setProperty(SVGRendererContextConstants.YPOS,
- new Integer(currentBPPosition + (int)pos.getY()));
- context.setProperty(SVGRendererContextConstants.WIDTH,
- new Integer((int)pos.getWidth()));
- context.setProperty(SVGRendererContextConstants.HEIGHT,
- new Integer((int) pos.getHeight()));
-
- renderXML(context, doc, ns);
}
/**
- * @see org.apache.fop.render.AbstractRenderer#renderLeader(Leader)
- */
- public void renderLeader(Leader area) {
- String style = "stroke:black;stroke-width:"
- + (area.getRuleThickness() / 1000) + ";";
- switch (area.getRuleStyle()) {
- case EN_DOTTED:
- style += "stroke-dasharray:1,1";
- break;
- case EN_DASHED:
- style += "stroke-dasharray:5,1";
- break;
- case EN_SOLID:
- break;
- case EN_DOUBLE:
- break;
- case EN_GROOVE:
- break;
- case EN_RIDGE:
- break;
+ * Returns the OutputStream corresponding to this page
+ * @param pageNumber 0-based page number
+ * @return the corresponding OutputStream
+ * @throws IOException In case of an I/O error
+ */
+ protected OutputStream getCurrentOutputStream(int pageNumber) throws IOException {
+ if (pageNumber == 0) {
+ return firstOutputStream;
+ } else {
+ return multiFileUtil.createOutputStream(pageNumber);
}
- Element line = SVGUtilities.createLine(svgDocument,
- currentIPPosition / 1000,
- (currentBPPosition + area.getOffset()
- - area.getRuleThickness() / 2) / 1000,
- (currentIPPosition + area.getIPD()) / 1000,
- (currentBPPosition + area.getOffset()
- - area.getRuleThickness() / 2) / 1000);
- line.setAttributeNS(null, "style", style);
- currentPageG.appendChild(line);
-
- super.renderLeader(area);
- }
- /**
- * @see org.apache.fop.render.AbstractRenderer#renderText(TextArea)
- */
- public void renderText(TextArea text) {
- Element textElement = SVGUtilities.createText(svgDocument,
- currentIPPosition / 1000,
- (currentBPPosition + text.getOffset()
- + text.getBaselineOffset()) / 1000,
- text.getText());
- currentPageG.appendChild(textElement);
-
- super.renderText(text);
- }
-
- /**
- * @see org.apache.fop.render.AbstractRenderer#renderCharacter(Character)
- */
-/* deprecated
- public void renderCharacter(org.apache.fop.area.inline.Character ch) {
- Element text = SVGUtilities.createText(svgDocument,
- currentIPPosition / 1000,
- (currentBPPosition + ch.getOffset()
- + ch.getBaselineOffset()) / 1000,
- "" + ch.getChar());
- currentPageG.appendChild(text);
-
- super.renderCharacter(ch);
- }
-*/
-
- /** @see org.apache.fop.render.AbstractRenderer */
- public String getMimeType() {
- return SVG_MIME_TYPE;
}
-
- /**
- * @see org.apache.fop.render.AbstractRenderer#startVParea(CTM, Rectangle2D)
- */
- protected void startVParea(CTM ctm, Rectangle2D clippingRect) {
- // TODO Auto-generated method stub
- }
-
- /**
- * @see org.apache.fop.render.AbstractRenderer#endVParea()
- */
- protected void endVParea() {
- // TODO Auto-generated method stub
- }
-
- protected void renderInlineAreaBackAndBorders(InlineArea area) {
- // TODO Auto-generated method stub
- }
-
+
}
-
Modified: xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/sandbox/org/apache/fop/render/svg/SVGSVGHandler.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/sandbox/org/apache/fop/render/svg/SVGSVGHandler.java?rev=579125&r1=579124&r2=579125&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/sandbox/org/apache/fop/render/svg/SVGSVGHandler.java (original)
+++ xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/src/sandbox/org/apache/fop/render/svg/SVGSVGHandler.java Tue Sep 25 01:41:51 2007
@@ -74,7 +74,7 @@
/** @see org.apache.fop.render.XMLHandler#getNamespace() */
public String getNamespace() {
- return SVGRenderer.SVG_NAMESPACE;
+ return SVGRenderer.MIME_TYPE;
}
Modified: xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/status.xml
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/status.xml?rev=579125&r1=579124&r2=579125&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/status.xml (original)
+++ xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/status.xml Tue Sep 25 01:41:51 2007
@@ -28,6 +28,18 @@
<changes>
<release version="FOP Trunk">
+ <action context="Code" dev="LF" type="fix">
+ Fixed a problem with disappearing footnotes inside hyphenated inlines (and footnotes with hyphenated inline child).
+ </action>
+ <action context="Code" dev="JM" type="add" fixes-bug="42067" due-to="Paul Vinkenoog">
+ Add support for exact positioning of internal PDF links.
+ </action>
+ <action context="Code" dev="JM" type="fix" fixes-bug="41434" due-to="Martin Kögler">
+ Fix PDF Genaration for non-ASCII compatible locales.
+ </action>
+ <action context="Code" dev="VH, JM" type="add" importance="high" fixes-bug="36934">
+ Add support for the collapsing-border model in tables.
+ </action>
<action context="Code" dev="JB" type="add" importance="high">
Add support for named destinations in PDF.
</action>
Modified: xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/test/layoutengine/disabled-testcases.xml
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/test/layoutengine/disabled-testcases.xml?rev=579125&r1=579124&r2=579125&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/test/layoutengine/disabled-testcases.xml (original)
+++ xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/test/layoutengine/disabled-testcases.xml Tue Sep 25 01:41:51 2007
@@ -105,12 +105,6 @@
regions.</description>
</testcase>
<testcase>
- <name>Footnotes swallowed in hyphenated fo:inlines</name>
- <file>footnote_in_inline.xml</file>
- <description>getChangedKnuthElements probably loses the footnote
- layout manager somewhere along the way.</description>
- </testcase>
- <testcase>
<name>Footnotes swallowed in lists</name>
<file>footnote_in_list.xml</file>
<description>Element lists for lists are created by combining the
Modified: xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/test/layoutengine/hyphenation-testcases/footnote_in_inline.xml
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/test/layoutengine/hyphenation-testcases/footnote_in_inline.xml?rev=579125&r1=579124&r2=579125&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/test/layoutengine/hyphenation-testcases/footnote_in_inline.xml (original)
+++ xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/test/layoutengine/hyphenation-testcases/footnote_in_inline.xml Tue Sep 25 01:41:51 2007
@@ -33,6 +33,7 @@
<fo:flow flow-name="xsl-region-body" language="en">
<fo:block>This is a block with a <fo:inline font-style="italic">footnote<fo:footnote><fo:inline font-size="50%" vertical-align="top">1</fo:inline><fo:footnote-body><fo:block><fo:inline font-size="50%" vertical-align="top">1</fo:inline>I'm a footnote!</fo:block></fo:footnote-body></fo:footnote></fo:inline> in it.</fo:block>
<fo:block hyphenate="true">This is a hyphenated block with a <fo:inline font-style="italic">footnote<fo:footnote><fo:inline font-size="50%" vertical-align="top">2</fo:inline><fo:footnote-body><fo:block><fo:inline font-size="50%" vertical-align="top">2</fo:inline>I'm a footnote!</fo:block></fo:footnote-body></fo:footnote></fo:inline> in it.</fo:block>
+ <fo:block hyphenate="true">This is a hyphenated block with a footnote<fo:footnote><fo:inline font-size="50%" vertical-align="top">hyphenation</fo:inline><fo:footnote-body><fo:block><fo:inline font-size="50%" vertical-align="top">hyphenation</fo:inline>I'm yet another footnote!</fo:block></fo:footnote-body></fo:footnote> in it.</fo:block>
<fo:block>This is another block without a footnote.</fo:block>
</fo:flow>
</fo:page-sequence>
@@ -42,6 +43,6 @@
<eval expected="1" xpath="count(//pageViewport)"/>
<!-- the footnotes -->
- <eval expected="2" xpath="count(//pageViewport[1]/page/regionViewport/regionBody/footnote/block)"/>
+ <eval expected="3" xpath="count(//pageViewport[1]/page/regionViewport/regionBody/footnote/block)"/>
</checks>
</testcase>
Modified: xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/test/layoutengine/standard-testcases/inline-level_id.xml
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/test/layoutengine/standard-testcases/inline-level_id.xml?rev=579125&r1=579124&r2=579125&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/test/layoutengine/standard-testcases/inline-level_id.xml (original)
+++ xmlgraphics/fop/branches/Temp_Interleaved_Page_Line_Breaking/test/layoutengine/standard-testcases/inline-level_id.xml Tue Sep 25 01:41:51 2007
@@ -65,7 +65,7 @@
</fo>
<checks>
<eval expected="inline1" xpath="//pageViewport[@nr=1]/page/regionViewport/regionBody/mainReference/span/flow/block[1]/lineArea/inlineparent/@prod-id"/>
- <!-- wrapper skipped -->
+ <eval expected="wrapper1" xpath="//pageViewport[@nr=1]/page/regionViewport/regionBody/mainReference/span/flow/block[2]/lineArea/inline/@prod-id"/>
<!--eval expected="inline-container1" xpath="//pageViewport[@nr=1]/page/regionViewport/regionBody/mainReference/span/flow/block[3]/lineArea/???/@prod-id"/-->
<eval expected="page-number1" xpath="//pageViewport[@nr=1]/page/regionViewport/regionBody/mainReference/span/flow/block[4]/lineArea/text[2]/@prod-id"/>
<eval expected="page-number-citation1" xpath="//pageViewport[@nr=1]/page/regionViewport/regionBody/mainReference/span/flow/block[5]/lineArea/text[2]/@prod-id"/>
---------------------------------------------------------------------
To unsubscribe, e-mail: fop-commits-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: fop-commits-help@xmlgraphics.apache.org