improved some resolving of references git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@195155 13f79535-47bb-0310-9956-ffa450edef68pull/30/head
/* | /* | ||||
* $Id$ | * $Id$ | ||||
* Copyright (C) 2001 The Apache Software Foundation. All rights reserved. | |||||
* Copyright (C) 2002 The Apache Software Foundation. All rights reserved. | |||||
* For details on use and redistribution please refer to the | * For details on use and redistribution please refer to the | ||||
* LICENSE file included with these sources. | * LICENSE file included with these sources. | ||||
*/ | */ | ||||
public class AreaTree { | public class AreaTree { | ||||
// allows for different models to deal with adding/rendering | // allows for different models to deal with adding/rendering | ||||
// in different situations | // in different situations | ||||
AreaTreeModel model; | |||||
private AreaTreeModel model; | |||||
// hashmap of arraylists containing pages with id area | // hashmap of arraylists containing pages with id area | ||||
HashMap idLocations = new HashMap(); | |||||
private HashMap idLocations = new HashMap(); | |||||
// list of id's yet to be resolved and arraylists of pages | // list of id's yet to be resolved and arraylists of pages | ||||
HashMap resolve = new HashMap(); | |||||
ArrayList treeExtensions = new ArrayList(); | |||||
private HashMap resolve = new HashMap(); | |||||
private ArrayList treeExtensions = new ArrayList(); | |||||
/** | |||||
* Create a render pages area tree model. | |||||
* @param rend the renderer that will be used | |||||
* @return RenderPagesModel the new area tree model | |||||
*/ | |||||
public RenderPagesModel createRenderPagesModel(Renderer rend) { | public RenderPagesModel createRenderPagesModel(Renderer rend) { | ||||
return new RenderPagesModel(rend); | return new RenderPagesModel(rend); | ||||
} | } | ||||
/** | |||||
* Create a new store pages model. | |||||
* @return StorePagesModel the new model | |||||
*/ | |||||
public static StorePagesModel createStorePagesModel() { | public static StorePagesModel createStorePagesModel() { | ||||
return new StorePagesModel(); | return new StorePagesModel(); | ||||
} | } | ||||
/** | |||||
* Set the tree model to use for this area tree. | |||||
* The different models can have different behaviour | |||||
* when pages area added and other changes. | |||||
* @param m the area tree model | |||||
*/ | |||||
public void setTreeModel(AreaTreeModel m) { | public void setTreeModel(AreaTreeModel m) { | ||||
model = m; | model = m; | ||||
} | } | ||||
/** | |||||
* Start a new page sequence. | |||||
* This signals that a new page sequence has started in the document. | |||||
* @param title the title of the new page sequence or null if no title | |||||
*/ | |||||
public void startPageSequence(Title title) { | public void startPageSequence(Title title) { | ||||
model.startPageSequence(title); | model.startPageSequence(title); | ||||
} | } | ||||
/** | |||||
* Add a new page to the area tree. | |||||
* @param page the page to add | |||||
*/ | |||||
public void addPage(PageViewport page) { | public void addPage(PageViewport page) { | ||||
model.addPage(page); | model.addPage(page); | ||||
} | } | ||||
/** | |||||
* Add an id reference pointing to a page viewport. | |||||
* @param id the id of the reference | |||||
* @param pv the page viewport that contains the id reference | |||||
*/ | |||||
public void addIDRef(String id, PageViewport pv) { | public void addIDRef(String id, PageViewport pv) { | ||||
ArrayList list = (ArrayList)idLocations.get(id); | |||||
if(list == null) { | |||||
List list = (List)idLocations.get(id); | |||||
if (list == null) { | |||||
list = new ArrayList(); | list = new ArrayList(); | ||||
idLocations.put(id, list); | idLocations.put(id, list); | ||||
} | } | ||||
list.add(pv); | list.add(pv); | ||||
ArrayList todo = (ArrayList)resolve.get(id); | |||||
if(todo != null) { | |||||
for(int count = 0; count < todo.size(); count++) { | |||||
List todo = (List)resolve.get(id); | |||||
if (todo != null) { | |||||
for (int count = 0; count < todo.size(); count++) { | |||||
Resolveable res = (Resolveable)todo.get(count); | Resolveable res = (Resolveable)todo.get(count); | ||||
res.resolve(id, list); | res.resolve(id, list); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/** | |||||
* Get the list of id references for an id. | |||||
* @param id the id to lookup | |||||
* @return the list of id references. | |||||
*/ | |||||
public List getIDReferences(String id) { | |||||
return (List)idLocations.get(id); | |||||
} | |||||
/** | |||||
* Add an unresolved object with a given id. | |||||
* @param id the id reference that needs resolving | |||||
* @param res the Resolveable object to resolve | |||||
*/ | |||||
public void addUnresolvedID(String id, Resolveable res) { | public void addUnresolvedID(String id, Resolveable res) { | ||||
ArrayList todo = (ArrayList)resolve.get(id); | ArrayList todo = (ArrayList)resolve.get(id); | ||||
if(todo == null) { | |||||
if (todo == null) { | |||||
todo = new ArrayList(); | todo = new ArrayList(); | ||||
resolve.put(id, todo); | resolve.put(id, todo); | ||||
} | } | ||||
todo.add(res); | todo.add(res); | ||||
} | } | ||||
/** | |||||
* Add a tree extension. | |||||
* This checks if the extension is resolveable and attempts | |||||
* to resolve or add the resolveable ids for later resolution. | |||||
* @param ext the tree extension to add. | |||||
*/ | |||||
public void addTreeExtension(TreeExt ext) { | public void addTreeExtension(TreeExt ext) { | ||||
treeExtensions.add(ext); | treeExtensions.add(ext); | ||||
if(ext.isResolveable()) { | |||||
if (ext.isResolveable()) { | |||||
Resolveable res = (Resolveable)ext; | Resolveable res = (Resolveable)ext; | ||||
String[] ids = res.getIDs(); | String[] ids = res.getIDs(); | ||||
for(int count = 0; count < ids.length; count++) { | |||||
if(idLocations.containsKey(ids[count])) { | |||||
for (int count = 0; count < ids.length; count++) { | |||||
if (idLocations.containsKey(ids[count])) { | |||||
res.resolve(ids[count], (ArrayList)idLocations.get(ids[count])); | res.resolve(ids[count], (ArrayList)idLocations.get(ids[count])); | ||||
} else { | } else { | ||||
ArrayList todo = (ArrayList)resolve.get(ids[count]); | ArrayList todo = (ArrayList)resolve.get(ids[count]); | ||||
if(todo == null) { | |||||
if (todo == null) { | |||||
todo = new ArrayList(); | todo = new ArrayList(); | ||||
resolve.put(ids[count], todo); | resolve.put(ids[count], todo); | ||||
} | } | ||||
todo.add(ext); | todo.add(ext); | ||||
} | } | ||||
} | } | ||||
} else { | |||||
handleTreeExtension(ext, TreeExt.IMMEDIATELY); | |||||
} | } | ||||
} | } | ||||
/** | |||||
* Handle a tree extension. | |||||
* This sends the extension to the model for handling. | |||||
* @param ext the tree extension to handle | |||||
* @param when when the extension should be handled by the model | |||||
*/ | |||||
public void handleTreeExtension(TreeExt ext, int when) { | public void handleTreeExtension(TreeExt ext, int when) { | ||||
// queue tree extension according to the when | // queue tree extension according to the when | ||||
model.addExtension(ext, when); | model.addExtension(ext, when); | ||||
} | } | ||||
/** | |||||
* Signal end of document. | |||||
* This indicates that the document is complete and any unresolved | |||||
* reference can be dealt with. | |||||
*/ | |||||
public void endDocument() { | public void endDocument() { | ||||
for(Iterator iter = resolve.keySet().iterator(); iter.hasNext(); ) { | |||||
for (Iterator iter = resolve.keySet().iterator(); iter.hasNext();) { | |||||
String id = (String)iter.next(); | String id = (String)iter.next(); | ||||
ArrayList list = (ArrayList)resolve.get(id); | ArrayList list = (ArrayList)resolve.get(id); | ||||
for(int count = 0; count < list.size(); count++) { | |||||
for (int count = 0; count < list.size(); count++) { | |||||
Resolveable res = (Resolveable)list.get(count); | Resolveable res = (Resolveable)list.get(count); | ||||
if(!res.isResolved()) { | |||||
if (!res.isResolved()) { | |||||
res.resolve(id, null); | res.resolve(id, null); | ||||
} | } | ||||
} | } | ||||
model.endDocument(); | model.endDocument(); | ||||
} | } | ||||
// this is the model for the area tree object | |||||
public static abstract class AreaTreeModel { | |||||
/** | |||||
* This is the model for the area tree object. | |||||
* The model implementation can handle the page sequence, | |||||
* page and extensions. | |||||
*/ | |||||
public abstract static class AreaTreeModel { | |||||
/** | |||||
* Start a page sequence on this model. | |||||
* @param title the title of the new page sequence | |||||
*/ | |||||
public abstract void startPageSequence(Title title); | public abstract void startPageSequence(Title title); | ||||
/** | |||||
* Add a page to this moel. | |||||
* @param page the page to add to the model. | |||||
*/ | |||||
public abstract void addPage(PageViewport page); | public abstract void addPage(PageViewport page); | ||||
/** | |||||
* Add an extension to this model. | |||||
* @param ext the extension to add | |||||
* @param when when the extension should be handled | |||||
*/ | |||||
public abstract void addExtension(TreeExt ext, int when); | public abstract void addExtension(TreeExt ext, int when); | ||||
/** | |||||
* Signal the end of the document for any processing. | |||||
*/ | |||||
public abstract void endDocument(); | public abstract void endDocument(); | ||||
} | } | ||||
// this class stores all the pages in the document | |||||
// for interactive agents | |||||
/** | |||||
* This class stores all the pages in the document | |||||
* for interactive agents. | |||||
* The pages are stored and can be retrieved in any order. | |||||
*/ | |||||
public static class StorePagesModel extends AreaTreeModel { | public static class StorePagesModel extends AreaTreeModel { | ||||
ArrayList pageSequence = null; | |||||
ArrayList titles = new ArrayList(); | |||||
ArrayList currSequence; | |||||
ArrayList extensions = new ArrayList(); | |||||
public StorePagesModel() {} | |||||
private ArrayList pageSequence = null; | |||||
private ArrayList titles = new ArrayList(); | |||||
private ArrayList currSequence; | |||||
private ArrayList extensions = new ArrayList(); | |||||
/** | |||||
* Create a new store pages model | |||||
*/ | |||||
public StorePagesModel() { | |||||
} | |||||
/** | |||||
* Start a new page sequence. | |||||
* This creates a new list for the pages in the new page sequence. | |||||
* @param title the title of the page sequence. | |||||
*/ | |||||
public void startPageSequence(Title title) { | public void startPageSequence(Title title) { | ||||
titles.add(title); | titles.add(title); | ||||
if (pageSequence == null) { | if (pageSequence == null) { | ||||
pageSequence.add(currSequence); | pageSequence.add(currSequence); | ||||
} | } | ||||
/** | |||||
* Add a page. | |||||
* @param page the page to add to the current page sequence | |||||
*/ | |||||
public void addPage(PageViewport page) { | public void addPage(PageViewport page) { | ||||
currSequence.add(page); | currSequence.add(page); | ||||
} | } | ||||
/** | |||||
* Get the page sequence count. | |||||
* @return the number of page sequences in the document. | |||||
*/ | |||||
public int getPageSequenceCount() { | public int getPageSequenceCount() { | ||||
return pageSequence.size(); | return pageSequence.size(); | ||||
} | } | ||||
/** | |||||
* Get the title for a page sequence. | |||||
* @param count the page sequence count | |||||
* @return the title of the page sequence | |||||
*/ | |||||
public Title getTitle(int count) { | public Title getTitle(int count) { | ||||
return (Title) titles.get(count); | return (Title) titles.get(count); | ||||
} | } | ||||
/** | |||||
* Get the page count. | |||||
* @param seq the page sequence to count. | |||||
* @return returns the number of pages in a page sequence | |||||
*/ | |||||
public int getPageCount(int seq) { | public int getPageCount(int seq) { | ||||
ArrayList sequence = (ArrayList) pageSequence.get(seq); | ArrayList sequence = (ArrayList) pageSequence.get(seq); | ||||
return sequence.size(); | return sequence.size(); | ||||
} | } | ||||
/** | |||||
* Get the page for a position in the document. | |||||
* @param seq the page sequence number | |||||
* @param count the page count in the sequence | |||||
* @return the PageViewport for the particular page | |||||
*/ | |||||
public PageViewport getPage(int seq, int count) { | public PageViewport getPage(int seq, int count) { | ||||
ArrayList sequence = (ArrayList) pageSequence.get(seq); | ArrayList sequence = (ArrayList) pageSequence.get(seq); | ||||
return (PageViewport) sequence.get(count); | return (PageViewport) sequence.get(count); | ||||
} | } | ||||
/** | |||||
* Add an extension to the store page model. | |||||
* The extension is stored so that it can be retrieved in the | |||||
* appropriate position. | |||||
* @param ext the extension to add | |||||
* @param when when the extension should be handled | |||||
*/ | |||||
public void addExtension(TreeExt ext, int when) { | public void addExtension(TreeExt ext, int when) { | ||||
int seq, page; | int seq, page; | ||||
switch(when) { | switch(when) { | ||||
extensions.add(ext); | extensions.add(ext); | ||||
} | } | ||||
/** | |||||
* Get the list of extensions that apply at a particular | |||||
* position in the document. | |||||
* @param seq the page sequence number | |||||
* @param count the page count in the sequence | |||||
* @return the list of extensions | |||||
*/ | |||||
public List getExtensions(int seq, int count) { | public List getExtensions(int seq, int count) { | ||||
return null; | return null; | ||||
} | } | ||||
/** | |||||
* Get the end of document extensions for this stroe pages model. | |||||
* @return the list of end extensions | |||||
*/ | |||||
public List getEndExtensions() { | public List getEndExtensions() { | ||||
return extensions; | return extensions; | ||||
} | } | ||||
/** | |||||
* End document, do nothing. | |||||
*/ | |||||
public void endDocument() { | public void endDocument() { | ||||
} | } | ||||
} | } | ||||
// this uses the store pages model to store the pages | |||||
// each page is either rendered if ready or prepared | |||||
// for later rendering | |||||
/** | |||||
* This uses the store pages model to store the pages | |||||
* each page is either rendered if ready or prepared | |||||
* for later rendering. | |||||
* Once a page is rendered it is cleared to release the | |||||
* contents but the PageViewport is retained. | |||||
*/ | |||||
public static class RenderPagesModel extends StorePagesModel { | public static class RenderPagesModel extends StorePagesModel { | ||||
Renderer renderer; | |||||
ArrayList prepared = new ArrayList(); | |||||
ArrayList pendingExt = new ArrayList(); | |||||
ArrayList endDocExt = new ArrayList(); | |||||
private Renderer renderer; | |||||
private ArrayList prepared = new ArrayList(); | |||||
private ArrayList pendingExt = new ArrayList(); | |||||
private ArrayList endDocExt = new ArrayList(); | |||||
/** | |||||
* Create a new render pages model with the given renderer. | |||||
* @param rend the renderer to render pages to | |||||
*/ | |||||
public RenderPagesModel(Renderer rend) { | public RenderPagesModel(Renderer rend) { | ||||
renderer = rend; | renderer = rend; | ||||
} | } | ||||
/** | |||||
* Start a new page sequence. | |||||
* This tells the renderer that a new page sequence has | |||||
* started with the given title. | |||||
* @param title the title of the new page sequence | |||||
*/ | |||||
public void startPageSequence(Title title) { | public void startPageSequence(Title title) { | ||||
super.startPageSequence(title); | super.startPageSequence(title); | ||||
renderer.startPageSequence(title); | renderer.startPageSequence(title); | ||||
} | } | ||||
/** | |||||
* Add a page to the render page model. | |||||
* If the page is finished it can be rendered immediately. | |||||
* If the page needs resolving then if the renderer supports | |||||
* out of order rendering it can prepare the page. Otherwise | |||||
* the page is added to a queue. | |||||
* @param page the page to add to the model | |||||
*/ | |||||
public void addPage(PageViewport page) { | public void addPage(PageViewport page) { | ||||
super.addPage(page); | super.addPage(page); | ||||
// if page finished | // if page finished | ||||
try { | try { | ||||
renderer.renderPage(page); | renderer.renderPage(page); | ||||
} catch(Exception e) { | |||||
} catch (Exception e) { | |||||
// use error handler to handle this FOP or IO Exception | // use error handler to handle this FOP or IO Exception | ||||
} | } | ||||
page.clear(); | page.clear(); | ||||
prepared.add(page); | prepared.add(page); | ||||
} | } | ||||
/** | |||||
* Add an extension to this model. | |||||
* If handle immediately then send directly to the renderer. | |||||
* The after page ones are handled after the next page is added. | |||||
* End of document extensions are added to a list to be | |||||
* handled at the end. | |||||
* @param ext the extension | |||||
* @param when when to render the extension | |||||
*/ | |||||
public void addExtension(TreeExt ext, int when) { | public void addExtension(TreeExt ext, int when) { | ||||
switch(when) { | switch(when) { | ||||
case TreeExt.IMMEDIATELY: | case TreeExt.IMMEDIATELY: | ||||
} | } | ||||
private void renderExtensions(ArrayList list) { | private void renderExtensions(ArrayList list) { | ||||
for(int count = 0; count < list.size(); count++) { | |||||
for (int count = 0; count < list.size(); count++) { | |||||
TreeExt ext = (TreeExt)list.get(count); | TreeExt ext = (TreeExt)list.get(count); | ||||
renderer.renderExtension(ext); | renderer.renderExtension(ext); | ||||
} | } | ||||
} | } | ||||
/** | |||||
* End the document. Render any end document extensions. | |||||
*/ | |||||
public void endDocument() { | public void endDocument() { | ||||
renderExtensions(endDocExt); | renderExtensions(endDocExt); | ||||
} | } |
package org.apache.fop.area; | package org.apache.fop.area; | ||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.io.Serializable; | |||||
public interface Resolveable { | |||||
/** | |||||
* Resolveable Interface. | |||||
* Classes that implement this can be resolved when | |||||
* an id is added to the area tree. | |||||
*/ | |||||
public interface Resolveable extends Serializable { | |||||
public boolean isResolved(); | public boolean isResolved(); | ||||
* @param pages the list of pages with the id area | * @param pages the list of pages with the id area | ||||
* may be null if not found | * may be null if not found | ||||
*/ | */ | ||||
public void resolve(String id, ArrayList pages); | |||||
public void resolve(String id, List pages); | |||||
} | } |
s_hmTraitInfo.put(ID_LINK, | s_hmTraitInfo.put(ID_LINK, | ||||
new TraitInfo("id-link", String.class)); | new TraitInfo("id-link", String.class)); | ||||
s_hmTraitInfo.put(INTERNAL_LINK, | s_hmTraitInfo.put(INTERNAL_LINK, | ||||
new TraitInfo("internal-link", String.class)); | |||||
new TraitInfo("internal-link", PageViewport.class)); | |||||
s_hmTraitInfo.put(EXTERNAL_LINK, | s_hmTraitInfo.put(EXTERNAL_LINK, | ||||
new TraitInfo("external-link", String.class)); | new TraitInfo("external-link", String.class)); | ||||
s_hmTraitInfo.put(FONT_NAME, | s_hmTraitInfo.put(FONT_NAME, |
* this inline parent. | * this inline parent. | ||||
*/ | */ | ||||
public class FilledArea extends InlineParent { | public class FilledArea extends InlineParent { | ||||
MinOptMax alloc; | |||||
int unitWidth; | |||||
private int unitWidth; | |||||
public FilledArea() { | public FilledArea() { | ||||
} | } | ||||
unitWidth = w; | unitWidth = w; | ||||
} | } | ||||
public void setAllocationIPD(MinOptMax all) { | |||||
alloc = all; | |||||
} | |||||
public MinOptMax getAllocationIPD() { | |||||
return alloc; | |||||
} | |||||
public List getChildAreas() { | public List getChildAreas() { | ||||
int units = (int)(getWidth() / unitWidth); | int units = (int)(getWidth() / unitWidth); | ||||
ArrayList newList = new ArrayList(); | ArrayList newList = new ArrayList(); |
/* | /* | ||||
* $Id$ | * $Id$ | ||||
* Copyright (C) 2001 The Apache Software Foundation. All rights reserved. | |||||
* Copyright (C) 2002 The Apache Software Foundation. All rights reserved. | |||||
* For details on use and redistribution please refer to the | * For details on use and redistribution please refer to the | ||||
* LICENSE file included with these sources. | * LICENSE file included with these sources. | ||||
*/ | */ | ||||
import org.apache.fop.traits.BorderProps; | import org.apache.fop.traits.BorderProps; | ||||
import org.apache.fop.fo.properties.VerticalAlign; | import org.apache.fop.fo.properties.VerticalAlign; | ||||
import java.util.List; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
/** | /** | ||||
int height; | int height; | ||||
protected int contentIPD = 0; | protected int contentIPD = 0; | ||||
// position within the line area, either top or baseline | |||||
int verticalPosition = VerticalAlign.BASELINE; | |||||
// offset position from top of parent area | |||||
int verticalPosition = 0; | |||||
// store properties in array list, need better solution | // store properties in array list, need better solution | ||||
ArrayList props = null; | ArrayList props = null; | ||||
this.contentIPD = ipd; | this.contentIPD = ipd; | ||||
} | } | ||||
public int getIPD() { | |||||
return this.contentIPD; | |||||
} | |||||
public void increaseIPD(int ipd) { | public void increaseIPD(int ipd) { | ||||
this.contentIPD += ipd; | this.contentIPD += ipd; | ||||
} | } |
import java.util.List; | import java.util.List; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
// this is an inline area that can have other inlines as children | |||||
/** | |||||
* Inline parent area. | |||||
* This is an inline area that can have other inlines as children. | |||||
*/ | |||||
public class InlineParent extends InlineArea { | public class InlineParent extends InlineArea { | ||||
protected ArrayList inlines = new ArrayList(); | protected ArrayList inlines = new ArrayList(); | ||||
public InlineParent() { | public InlineParent() { | ||||
} | } | ||||
/** | |||||
* Render this area. | |||||
* @param renderer the renderer to render this area in | |||||
*/ | |||||
public void render(Renderer renderer) { | public void render(Renderer renderer) { | ||||
renderer.renderInlineParent(this); | renderer.renderInlineParent(this); | ||||
} | } | ||||
/** | /** | ||||
* Override generic Area method. | * Override generic Area method. | ||||
*/ | */ |
import org.apache.fop.area.PageViewport; | import org.apache.fop.area.PageViewport; | ||||
import org.apache.fop.area.Resolveable; | import org.apache.fop.area.Resolveable; | ||||
import org.apache.fop.area.Trait; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
public class UnresolvedPageNumber extends Word implements Resolveable { | public class UnresolvedPageNumber extends Word implements Resolveable { | ||||
boolean resolved = false; | |||||
String pageRefId; | |||||
private boolean resolved = false; | |||||
private String pageRefId; | |||||
public UnresolvedPageNumber(String id) { | public UnresolvedPageNumber(String id) { | ||||
pageRefId = id; | pageRefId = id; | ||||
return new String[] {pageRefId}; | return new String[] {pageRefId}; | ||||
} | } | ||||
public void resolve(String id, ArrayList pages) { | |||||
public void resolve(String id, List pages) { | |||||
resolved = true; | resolved = true; | ||||
PageViewport page = (PageViewport)pages.get(0); | |||||
String str = page.getPageNumber(); | |||||
word = str; | |||||
if(pages != null) { | |||||
PageViewport page = (PageViewport)pages.get(0); | |||||
String str = page.getPageNumber(); | |||||
word = str; | |||||
// update ipd | |||||
String name = (String) getTrait(Trait.FONT_NAME); | |||||
int size = ((Integer) getTrait(Trait.FONT_SIZE)).intValue(); | |||||
//FontMetric metrics = fontInfo.getMetricsFor(name); | |||||
//FontState fs = new FontState(name, metrics, size); | |||||
} | |||||
} | } | ||||
public boolean isResolved() { | public boolean isResolved() { |
return (String[])idRefs.keySet().toArray(new String[] {}); | return (String[])idRefs.keySet().toArray(new String[] {}); | ||||
} | } | ||||
public void resolve(String id, ArrayList pages) { | |||||
public void resolve(String id, List pages) { | |||||
if(!id.equals(idRef)) { | if(!id.equals(idRef)) { | ||||
BookmarkData bd = (BookmarkData)idRefs.get(id); | BookmarkData bd = (BookmarkData)idRefs.get(id); | ||||
idRefs.remove(id); | idRefs.remove(id); |
import org.apache.fop.fo.properties.*; | import org.apache.fop.fo.properties.*; | ||||
import org.apache.fop.layout.*; | import org.apache.fop.layout.*; | ||||
import org.apache.fop.datatypes.ColorType; | import org.apache.fop.datatypes.ColorType; | ||||
import org.apache.fop.area.inline.InlineParent; | |||||
import org.apache.fop.area.Trait; | |||||
import org.apache.fop.area.Resolveable; | |||||
import org.apache.fop.area.PageViewport; | |||||
import org.apache.fop.area.Area; | |||||
import org.apache.fop.layoutmgr.InlineStackingLayoutManager; | |||||
import org.apache.fop.layoutmgr.LMiter; | |||||
import org.apache.fop.layoutmgr.LayoutManager; | |||||
// Java | // Java | ||||
import java.util.Enumeration; | import java.util.Enumeration; | ||||
import java.awt.Rectangle; | import java.awt.Rectangle; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.ArrayList; | |||||
public class BasicLink extends Inline { | public class BasicLink extends Inline { | ||||
String link = null; | |||||
boolean external = false; | |||||
public BasicLink(FONode parent) { | public BasicLink(FONode parent) { | ||||
super(parent); | super(parent); | ||||
// add start and end properties for the link | // add start and end properties for the link | ||||
public void addLayoutManager(List lms) { | public void addLayoutManager(List lms) { | ||||
super.addLayoutManager(lms); | |||||
setup(); | |||||
lms.add(new InlineStackingLayoutManager(this, | |||||
new LMiter(children.listIterator())) { | |||||
protected InlineParent createArea() { | |||||
InlineParent area = super.createArea(); | |||||
setupLinkArea(parentLM, area); | |||||
return area; | |||||
} | |||||
}); | |||||
} | |||||
protected void setupLinkArea(LayoutManager parentLM, InlineParent area) { | |||||
if(link == null) { | |||||
return; | |||||
} | |||||
if(external) { | |||||
area.addTrait(Trait.EXTERNAL_LINK, link); | |||||
} else { | |||||
PageViewport page = parentLM.resolveRefID(link); | |||||
if(page != null) { | |||||
area.addTrait(Trait.INTERNAL_LINK, page); | |||||
} else { | |||||
LinkResolver res = new LinkResolver(link, area); | |||||
parentLM.addUnresolvedArea(link, res); | |||||
} | |||||
} | |||||
} | } | ||||
public void setup() { | public void setup() { | ||||
// this.properties.get("baseline-shift"); | // this.properties.get("baseline-shift"); | ||||
// this.properties.get("destination-place-offset"); | // this.properties.get("destination-place-offset"); | ||||
// this.properties.get("dominant-baseline"); | // this.properties.get("dominant-baseline"); | ||||
// this.properties.get("external-destination"); | |||||
String ext = properties.get("external-destination").getString(); | |||||
setupID(); | setupID(); | ||||
// this.properties.get("indicate-destination"); | |||||
// this.properties.get("internal-destination"); | |||||
// this.properties.get("indicate-destination"); | |||||
String internal = properties.get("internal-destination").getString(); | |||||
if(ext.length() > 0) { | |||||
link = ext; | |||||
external = true; | |||||
} else if(internal.length() > 0) { | |||||
link = internal; | |||||
} else { | |||||
getLogger().error("basic-link requires an internal or external destination"); | |||||
} | |||||
// this.properties.get("keep-together"); | // this.properties.get("keep-together"); | ||||
// this.properties.get("keep-with-next"); | // this.properties.get("keep-with-next"); | ||||
// this.properties.get("keep-with-previous"); | // this.properties.get("keep-with-previous"); | ||||
// this.properties.get("line-height"); | // this.properties.get("line-height"); | ||||
// this.properties.get("line-height-shift-adjustment"); | // this.properties.get("line-height-shift-adjustment"); | ||||
// this.properties.get("show-destination"); | |||||
// this.properties.get("target-processing-context"); | |||||
// this.properties.get("target-presentation-context"); | |||||
// this.properties.get("target-stylesheet"); | |||||
// this.properties.get("show-destination"); | |||||
// this.properties.get("target-processing-context"); | |||||
// this.properties.get("target-presentation-context"); | |||||
// this.properties.get("target-stylesheet"); | |||||
} | |||||
protected static class LinkResolver implements Resolveable { | |||||
private boolean resolved = false; | |||||
private String idRef; | |||||
// NOTE: there will be a problem with serialization | |||||
private Area area; | |||||
public LinkResolver(String id, Area a) { | |||||
idRef = id; | |||||
area = a; | |||||
} | |||||
public boolean isResolved() { | |||||
return resolved; | |||||
} | |||||
public String[] getIDs() { | |||||
return new String[] {idRef}; | |||||
} | |||||
/** | |||||
* Resolve by removing the id link and replacing with | |||||
* an internal link. | |||||
*/ | |||||
public void resolve(String id, List pages) { | |||||
if(idRef.equals(id) && pages != null) { | |||||
PageViewport page = (PageViewport)pages.get(0); | |||||
area.addTrait(Trait.INTERNAL_LINK, page); | |||||
} | |||||
} | |||||
} | } | ||||
} | } | ||||
public void addLayoutManager(List list) { | public void addLayoutManager(List list) { | ||||
LeafNodeLayoutManager lm = new LeafNodeLayoutManager(this) { | LeafNodeLayoutManager lm = new LeafNodeLayoutManager(this) { | ||||
public InlineArea get(LayoutContext context) { | public InlineArea get(LayoutContext context) { | ||||
int refIPD = context.getRefIPD(); | |||||
return getInlineArea(refIPD); | |||||
return getInlineArea(); | |||||
} | |||||
protected MinOptMax getAllocationIPD(int refIPD) { | |||||
return getAllocIPD(refIPD); | |||||
} | } | ||||
}; | }; | ||||
lm.setAlignment(properties.get("leader-alignment").getEnum()); | lm.setAlignment(properties.get("leader-alignment").getEnum()); | ||||
list.add(lm); | list.add(lm); | ||||
} | } | ||||
protected InlineArea getInlineArea(int refIPD) { | |||||
protected InlineArea getInlineArea() { | |||||
if(leaderArea == null) { | if(leaderArea == null) { | ||||
createLeaderArea(); | createLeaderArea(); | ||||
} | } | ||||
MinOptMax alloc = getAllocationIPD(refIPD); | |||||
if(leaderArea instanceof Stretch) { | |||||
((Stretch)leaderArea).setAllocationIPD(alloc); | |||||
} else if(leaderArea instanceof FilledArea) { | |||||
((FilledArea)leaderArea).setAllocationIPD(alloc); | |||||
} | |||||
leaderArea.setWidth(alloc.opt); | |||||
return leaderArea; | return leaderArea; | ||||
} | } | ||||
leaderArea = leader; | leaderArea = leader; | ||||
} else if (leaderPattern == LeaderPattern.SPACE) { | } else if (leaderPattern == LeaderPattern.SPACE) { | ||||
Space space = new Space(); | |||||
leaderArea = space; | |||||
leaderArea = new Space(); | |||||
} else if(leaderPattern == LeaderPattern.DOTS) { | } else if(leaderPattern == LeaderPattern.DOTS) { | ||||
Word w = new Word(); | Word w = new Word(); | ||||
char dot = '.'; // userAgent.getLeaderDotChar(); | char dot = '.'; // userAgent.getLeaderDotChar(); | ||||
} | } | ||||
protected MinOptMax getAllocationIPD(int ipd) { | |||||
protected MinOptMax getAllocIPD(int ipd) { | |||||
// length of the leader | // length of the leader | ||||
int opt = getLength("leader-length.optimum", ipd); | int opt = getLength("leader-length.optimum", ipd); | ||||
int min = getLength("leader-length.minimum", ipd); | int min = getLength("leader-length.minimum", ipd); |
import org.apache.fop.apps.FOPException; | import org.apache.fop.apps.FOPException; | ||||
import org.apache.fop.layoutmgr.LeafNodeLayoutManager; | import org.apache.fop.layoutmgr.LeafNodeLayoutManager; | ||||
import org.apache.fop.area.inline.InlineArea; | import org.apache.fop.area.inline.InlineArea; | ||||
import org.apache.fop.area.PageViewport; | |||||
import org.apache.fop.util.CharUtilities; | import org.apache.fop.util.CharUtilities; | ||||
import org.apache.fop.apps.StructureHandler; | import org.apache.fop.apps.StructureHandler; | ||||
import org.apache.fop.layoutmgr.LayoutContext; | import org.apache.fop.layoutmgr.LayoutContext; | ||||
} | } | ||||
public void addAreas(PositionIterator posIter, | public void addAreas(PositionIterator posIter, | ||||
LayoutContext context) { | |||||
LayoutContext context) { | |||||
super.addAreas(posIter, context); | super.addAreas(posIter, context); | ||||
if(unresolved) { | |||||
parentLM.addUnresolvedArea(refId, (Resolveable)inline); | |||||
if (unresolved) { | |||||
parentLM.addUnresolvedArea(refId, | |||||
(Resolveable) inline); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
); | ); | ||||
} | } | ||||
// is id can be resolved then simply return a word, otherwise | |||||
// if id can be resolved then simply return a word, otherwise | |||||
// return a resolveable area | // return a resolveable area | ||||
private InlineArea getInlineArea(LayoutManager parentLM) { | private InlineArea getInlineArea(LayoutManager parentLM) { | ||||
if (refId.equals("")) { | if (refId.equals("")) { | ||||
getLogger().error("page-number-citation must contain \"ref-id\""); | getLogger().error("page-number-citation must contain \"ref-id\""); | ||||
return null; | return null; | ||||
} | } | ||||
String str = parentLM.resolveRefID(refId); | |||||
if(str != null) { | |||||
// get page string from parent, build area | |||||
Word word = new Word(); | |||||
inline = word; | |||||
int width = getStringWidth(str); | |||||
word.setWord(str); | |||||
inline.setIPD(width); | |||||
inline.setHeight(fontState.getAscender() - | |||||
fontState.getDescender()); | |||||
inline.setOffset(fontState.getAscender()); | |||||
inline.addTrait(Trait.FONT_NAME, fontState.getFontName()); | |||||
inline.addTrait(Trait.FONT_SIZE, | |||||
new Integer(fontState.getFontSize())); | |||||
PageViewport page = parentLM.resolveRefID(refId); | |||||
if (page != null) { | |||||
String str = page.getPageNumber(); | |||||
// get page string from parent, build area | |||||
Word word = new Word(); | |||||
inline = word; | |||||
int width = getStringWidth(str); | |||||
word.setWord(str); | |||||
inline.setIPD(width); | |||||
inline.setHeight(fontState.getAscender() - | |||||
fontState.getDescender()); | |||||
inline.setOffset(fontState.getAscender()); | |||||
inline.addTrait(Trait.FONT_NAME, fontState.getFontName()); | |||||
inline.addTrait(Trait.FONT_SIZE, | |||||
new Integer(fontState.getFontSize())); | |||||
unresolved = false; | |||||
} else { | } else { | ||||
unresolved = true; | |||||
inline = new UnresolvedPageNumber(refId); | |||||
str = "MMM"; // reserve three spaces for page number | |||||
int width = getStringWidth(str); | |||||
inline.setIPD(width); | |||||
inline.setHeight(fontState.getAscender() - | |||||
fontState.getDescender()); | |||||
inline.setOffset(fontState.getAscender()); | |||||
inline.addTrait(Trait.FONT_NAME, fontState.getFontName()); | |||||
inline.addTrait(Trait.FONT_SIZE, | |||||
new Integer(fontState.getFontSize())); | |||||
unresolved = true; | |||||
inline = new UnresolvedPageNumber(refId); | |||||
String str = "MMM"; // reserve three spaces for page number | |||||
int width = getStringWidth(str); | |||||
inline.setIPD(width); | |||||
inline.setHeight(fontState.getAscender() - | |||||
fontState.getDescender()); | |||||
inline.setOffset(fontState.getAscender()); | |||||
inline.addTrait(Trait.FONT_NAME, fontState.getFontName()); | |||||
inline.addTrait(Trait.FONT_SIZE, | |||||
new Integer(fontState.getFontSize())); | |||||
} | } | ||||
return inline; | return inline; | ||||
} | } |
import org.apache.fop.fo.FONode; | import org.apache.fop.fo.FONode; | ||||
import org.apache.fop.area.Area; | import org.apache.fop.area.Area; | ||||
import org.apache.fop.area.Resolveable; | import org.apache.fop.area.Resolveable; | ||||
import org.apache.fop.area.PageViewport; | |||||
import org.apache.fop.fo.PropertyManager; | import org.apache.fop.fo.PropertyManager; | ||||
import java.util.ListIterator; | import java.util.ListIterator; | ||||
return parentLM.getCurrentPageNumber(); | return parentLM.getCurrentPageNumber(); | ||||
} | } | ||||
public String resolveRefID(String ref) { | |||||
public PageViewport resolveRefID(String ref) { | |||||
return parentLM.resolveRefID(ref); | return parentLM.resolveRefID(ref); | ||||
} | } | ||||
import org.apache.fop.area.Area; | import org.apache.fop.area.Area; | ||||
import org.apache.fop.area.MinOptMax; | import org.apache.fop.area.MinOptMax; | ||||
import org.apache.fop.area.Resolveable; | import org.apache.fop.area.Resolveable; | ||||
import org.apache.fop.area.PageViewport; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
public class ContentLayoutManager implements LayoutManager { | public class ContentLayoutManager implements LayoutManager { | ||||
Area holder; | Area holder; | ||||
int stackSize; | int stackSize; | ||||
LayoutManager parentLM; | |||||
public ContentLayoutManager(Area area) { | public ContentLayoutManager(Area area) { | ||||
holder = area; | holder = area; | ||||
} | } | ||||
public void setParentLM(LayoutManager lm) { | public void setParentLM(LayoutManager lm) { | ||||
parentLM = lm; | |||||
} | } | ||||
public boolean canBreakBefore(LayoutContext lc) { | public boolean canBreakBefore(LayoutContext lc) { | ||||
} | } | ||||
public String getCurrentPageNumber() { | public String getCurrentPageNumber() { | ||||
return ""; | |||||
return parentLM.getCurrentPageNumber(); | |||||
} | } | ||||
public String resolveRefID(String ref) { | |||||
return null; | |||||
public PageViewport resolveRefID(String ref) { | |||||
return parentLM.resolveRefID(ref);; | |||||
} | } | ||||
public void addIDToPage(String id) { | public void addIDToPage(String id) { | ||||
parentLM.addIDToPage(id); | |||||
} | } | ||||
public void addUnresolvedArea(String id, Resolveable res) { | public void addUnresolvedArea(String id, Resolveable res) { | ||||
parentLM.addUnresolvedArea(id, res); | |||||
} | } | ||||
} | } | ||||
MinOptMax stackSize = new MinOptMax(); | MinOptMax stackSize = new MinOptMax(); | ||||
while ((curLM = getChildLM()) != null) { | while ((curLM = getChildLM()) != null) { | ||||
if(curLM.generatesInlineAreas()) { | |||||
// problem | |||||
curLM.setFinished(true); | |||||
continue; | |||||
} | |||||
// Make break positions and return page break | // Make break positions and return page break | ||||
// Set up a LayoutContext | // Set up a LayoutContext | ||||
MinOptMax bpd = context.getStackLimit(); | MinOptMax bpd = context.getStackLimit(); |
} | } | ||||
*****/ | *****/ | ||||
protected InlineParent createArea() { | |||||
return new InlineParent(); | |||||
} | |||||
/** | /** | ||||
* Generate and add areas to parent area. | * Generate and add areas to parent area. | ||||
* Set size of each area. | * Set size of each area. | ||||
*/ | */ | ||||
public void addAreas(PositionIterator parentIter, | public void addAreas(PositionIterator parentIter, | ||||
LayoutContext context) { | LayoutContext context) { | ||||
setCurrentArea(new InlineParent()); | |||||
InlineParent parent = createArea(); | |||||
parent.setHeight(context.getLineHeight()); | |||||
parent.setOffset(0); | |||||
setCurrentArea(parent); | |||||
setChildContext(new LayoutContext(context)); // Store current value | setChildContext(new LayoutContext(context)); // Store current value | ||||
import org.apache.fop.area.Area; | import org.apache.fop.area.Area; | ||||
import org.apache.fop.area.Resolveable; | import org.apache.fop.area.Resolveable; | ||||
import org.apache.fop.area.PageViewport; | |||||
/** | /** | ||||
* The interface for all LayoutManagers. | * The interface for all LayoutManagers. | ||||
public String getCurrentPageNumber(); | public String getCurrentPageNumber(); | ||||
public String resolveRefID(String ref); | |||||
public PageViewport resolveRefID(String ref); | |||||
public void addIDToPage(String id); | public void addIDToPage(String id); | ||||
private InlineArea curArea = null; | private InlineArea curArea = null; | ||||
private int alignment; | private int alignment; | ||||
private int lead; | private int lead; | ||||
private MinOptMax ipd; | |||||
public LeafNodeLayoutManager(FObj fobj) { | public LeafNodeLayoutManager(FObj fobj) { | ||||
super(fobj); | super(fobj); | ||||
BreakPoss.CAN_BREAK_AFTER | | BreakPoss.CAN_BREAK_AFTER | | ||||
BreakPoss.CAN_BREAK_BEFORE | BreakPoss.ISFIRST | | BreakPoss.CAN_BREAK_BEFORE | BreakPoss.ISFIRST | | ||||
BreakPoss.ISLAST); | BreakPoss.ISLAST); | ||||
bp.setStackingSize(curArea.getAllocationIPD()); | |||||
ipd = getAllocationIPD(context.getRefIPD()); | |||||
bp.setStackingSize(ipd); | |||||
bp.setNonStackingSize(curArea.getAllocationBPD()); | bp.setNonStackingSize(curArea.getAllocationBPD()); | ||||
bp.setTrailingSpace(new SpaceSpecifier(false)); | bp.setTrailingSpace(new SpaceSpecifier(false)); | ||||
return bp; | return bp; | ||||
} | } | ||||
protected MinOptMax getAllocationIPD(int refIPD) { | |||||
return new MinOptMax(curArea.getIPD()); | |||||
} | |||||
public void resetPosition(Position resetPos) { | public void resetPosition(Position resetPos) { | ||||
// only reset if setting null, start again | // only reset if setting null, start again | ||||
if(resetPos == null) { | if(resetPos == null) { | ||||
addID(); | addID(); | ||||
offsetArea(context); | |||||
widthAdjustArea(context); | |||||
while (posIter.hasNext()) { | |||||
posIter.next(); | |||||
} | |||||
} | |||||
protected void offsetArea(LayoutContext context) { | |||||
int bpd = curArea.getHeight(); | int bpd = curArea.getHeight(); | ||||
switch(alignment) { | switch(alignment) { | ||||
case VerticalAlign.MIDDLE: | case VerticalAlign.MIDDLE: | ||||
curArea.setOffset(context.getBaseline() - bpd); | curArea.setOffset(context.getBaseline() - bpd); | ||||
break; | break; | ||||
} | } | ||||
} | |||||
protected void widthAdjustArea(LayoutContext context) { | |||||
double dAdjust = context.getIPDAdjust(); | double dAdjust = context.getIPDAdjust(); | ||||
MinOptMax ipd = curArea.getAllocationIPD(); | |||||
int width = ipd.opt; | int width = ipd.opt; | ||||
if(dAdjust < 0) { | if(dAdjust < 0) { | ||||
width = (int)(width + dAdjust * (ipd.opt - ipd.min)); | width = (int)(width + dAdjust * (ipd.opt - ipd.min)); | ||||
width = (int)(width + dAdjust * (ipd.max - ipd.opt)); | width = (int)(width + dAdjust * (ipd.max - ipd.opt)); | ||||
} | } | ||||
curArea.setWidth(width); | curArea.setWidth(width); | ||||
while (posIter.hasNext()) { | |||||
posIter.next(); | |||||
} | |||||
} | } | ||||
public boolean canBreakBefore(LayoutContext context) { | public boolean canBreakBefore(LayoutContext context) { |
return "" + pageCount; | return "" + pageCount; | ||||
} | } | ||||
public String resolveRefID(String ref) { | |||||
public PageViewport resolveRefID(String ref) { | |||||
List list = areaTree.getIDReferences(ref); | |||||
if(list != null && list.size() > 0) { | |||||
return (PageViewport)list.get(0); | |||||
} | |||||
return null; | return null; | ||||
} | } | ||||
public void addUnresolvedArea(String id, Resolveable res) { | public void addUnresolvedArea(String id, Resolveable res) { | ||||
// add unresolved to tree | // add unresolved to tree | ||||
// should really add to the page viewport so it can serialize | |||||
areaTree.addUnresolvedID(id, res); | areaTree.addUnresolvedID(id, res); | ||||
} | } | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.Iterator; | import java.util.Iterator; | ||||
import java.awt.Rectangle; | |||||
import java.awt.geom.Rectangle2D; | |||||
/** | /** | ||||
* class representing a PDF document. | * class representing a PDF document. | ||||
* @param linkType the link type | * @param linkType the link type | ||||
* @return the PDFLink object created | * @return the PDFLink object created | ||||
*/ | */ | ||||
public PDFLink makeLink(Rectangle rect, String destination, | |||||
public PDFLink makeLink(Rectangle2D rect, String destination, | |||||
int linkType) { | int linkType) { | ||||
PDFLink linkObject; | PDFLink linkObject; | ||||
this.trailerObjects.add(object); | this.trailerObjects.add(object); | ||||
} | } | ||||
public PDFLink makeLink(Rectangle rect, String page, String dest) { | |||||
public PDFLink makeLink(Rectangle2D rect, String page, String dest) { | |||||
PDFLink link = new PDFLink(++this.objectcount, rect); | PDFLink link = new PDFLink(++this.objectcount, rect); | ||||
this.objects.add(link); | this.objects.add(link); | ||||
package org.apache.fop.pdf; | package org.apache.fop.pdf; | ||||
// Java | // Java | ||||
import java.awt.Rectangle; | |||||
import java.awt.geom.Rectangle2D; | |||||
/** | /** | ||||
* class representing an /Annot object of /Subtype /Link | * class representing an /Annot object of /Subtype /Link | ||||
* @param number the object's number | * @param number the object's number | ||||
* @param producer the application producing the PDF | * @param producer the application producing the PDF | ||||
*/ | */ | ||||
public PDFLink(int number, Rectangle r) { | |||||
public PDFLink(int number, Rectangle2D r) { | |||||
/* generic creation of PDF object */ | /* generic creation of PDF object */ | ||||
super(number); | super(number); | ||||
this.ulx = r.x; | |||||
this.uly = r.y; | |||||
this.brx = r.x + r.width; | |||||
this.bry = r.y - r.height; | |||||
this.ulx = (float)r.getX(); | |||||
this.uly = (float)r.getY(); | |||||
this.brx = (float)(r.getX() + r.getWidth()); | |||||
this.bry = (float)(r.getY() - r.getHeight()); | |||||
this.color = "0 0 0"; // just for now | this.color = "0 0 0"; // just for now | ||||
} | } |
/* | /* | ||||
* $Id$ | * $Id$ | ||||
* Copyright (C) 2001 The Apache Software Foundation. All rights reserved. | |||||
* Copyright (C) 2001-2002 The Apache Software Foundation. All rights reserved. | |||||
* For details on use and redistribution please refer to the | * For details on use and redistribution please refer to the | ||||
* LICENSE file included with these sources. | * LICENSE file included with these sources. | ||||
*/ | */ | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.OutputStream; | import java.io.OutputStream; | ||||
import java.awt.geom.Rectangle2D; | import java.awt.geom.Rectangle2D; | ||||
import java.awt.Rectangle; | |||||
import java.awt.geom.AffineTransform; | import java.awt.geom.AffineTransform; | ||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.List; | import java.util.List; | ||||
public void renderExtension(TreeExt ext) { | public void renderExtension(TreeExt ext) { | ||||
// render bookmark extension | // render bookmark extension | ||||
if(ext instanceof BookmarkData) { | |||||
if (ext instanceof BookmarkData) { | |||||
renderRootExtensions((BookmarkData)ext); | renderRootExtensions((BookmarkData)ext); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
public void startPageSequence(Title seqTitle) { | public void startPageSequence(Title seqTitle) { | ||||
if(seqTitle != null) { | |||||
if (seqTitle != null) { | |||||
String str = convertTitleToString(seqTitle); | String str = convertTitleToString(seqTitle); | ||||
PDFInfo info = this.pdfDoc.getInfo(); | PDFInfo info = this.pdfDoc.getInfo(); | ||||
info.setTitle(str); | info.setTitle(str); | ||||
currentStream.add("ET\n"); | currentStream.add("ET\n"); | ||||
if(bv.getClip()) { | |||||
if (bv.getClip()) { | |||||
Rectangle2D rect = bv.getBounds(); | Rectangle2D rect = bv.getBounds(); | ||||
currentStream.add("q\n"); | currentStream.add("q\n"); | ||||
Rectangle2D rect = bv.getBounds(); | Rectangle2D rect = bv.getBounds(); | ||||
if(ctm != null) { | |||||
if (ctm != null) { | |||||
currentIPPosition = 0; | currentIPPosition = 0; | ||||
currentBPPosition = 0; | currentBPPosition = 0; | ||||
double[] vals = ctm.toArray(); | double[] vals = ctm.toArray(); | ||||
boolean aclock = vals[2] == 1.0; | boolean aclock = vals[2] == 1.0; | ||||
if(vals[2] == 1.0) { | |||||
if (vals[2] == 1.0) { | |||||
ctm = ctm.translate(-saveBP - rect.getHeight(), -saveIP); | ctm = ctm.translate(-saveBP - rect.getHeight(), -saveIP); | ||||
} else if(vals[0] == -1.0) { | |||||
} else if (vals[0] == -1.0) { | |||||
ctm = ctm.translate(-saveIP - rect.getWidth(), -saveBP - rect.getHeight()); | ctm = ctm.translate(-saveIP - rect.getWidth(), -saveBP - rect.getHeight()); | ||||
} else { | } else { | ||||
ctm = ctm.translate(saveBP, saveIP - rect.getWidth()); | ctm = ctm.translate(saveBP, saveIP - rect.getWidth()); | ||||
} | } | ||||
} | } | ||||
if(bv.getClip()) { | |||||
if (bv.getClip()) { | |||||
currentStream.add("q\n"); | currentStream.add("q\n"); | ||||
float x = (float)rect.getX() / 1000f; | float x = (float)rect.getX() / 1000f; | ||||
float y = (float)rect.getY() / 1000f; | float y = (float)rect.getY() / 1000f; | ||||
clip(x, y, width, height); | clip(x, y, width, height); | ||||
} | } | ||||
if(ctm != null) { | |||||
if (ctm != null) { | |||||
startVParea(ctm); | startVParea(ctm); | ||||
} | } | ||||
renderBlocks(children); | renderBlocks(children); | ||||
if(ctm != null) { | |||||
if (ctm != null) { | |||||
endVParea(); | endVParea(); | ||||
} | } | ||||
if (bv.getClip()) { | if (bv.getClip()) { | ||||
currentStream.add("Q\n"); | currentStream.add("Q\n"); | ||||
} | } | ||||
if(ctm != null) { | |||||
if (ctm != null) { | |||||
currentStream.add("BT\n"); | currentStream.add("BT\n"); | ||||
} | } | ||||
// clip if necessary | // clip if necessary | ||||
if(rect != null) { | |||||
if (rect != null) { | |||||
currentIPPosition = saveIP; | currentIPPosition = saveIP; | ||||
currentBPPosition = saveBP; | currentBPPosition = saveBP; | ||||
currentBPPosition += (int)(rect.getHeight()); | currentBPPosition += (int)(rect.getHeight()); | ||||
} | } | ||||
} | } | ||||
/** | |||||
* Clip an area. | |||||
* write a clipping operation given coordinates in the current | |||||
* transform. | |||||
* @param x the x coordinate | |||||
* @param y the y coordinate | |||||
* @param width the width of the area | |||||
* @param height the height of the area | |||||
*/ | |||||
protected void clip(float x, float y, float width, float height) { | protected void clip(float x, float y, float width, float height) { | ||||
currentStream.add(x + " " + y + " m\n"); | currentStream.add(x + " " + y + " m\n"); | ||||
currentStream.add((x + width) + " " + y + " l\n"); | currentStream.add((x + width) + " " + y + " l\n"); | ||||
closeText(); | closeText(); | ||||
} | } | ||||
/** | |||||
* 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) { | |||||
Object tr = ip.getTrait(Trait.INTERNAL_LINK); | |||||
boolean internal = false; | |||||
String dest = null; | |||||
if (tr == null) { | |||||
dest = (String)ip.getTrait(Trait.EXTERNAL_LINK); | |||||
} else { | |||||
PageViewport pv = (PageViewport)tr; | |||||
dest = (String)pageReferences.get(pv); | |||||
internal = true; | |||||
} | |||||
if (dest != null) { | |||||
float start = currentBlockIPPosition; | |||||
float top = (ip.getOffset() + currentBPPosition) / 1000f; | |||||
float height = ip.getHeight() / 1000f; | |||||
super.renderInlineParent(ip); | |||||
float width = (currentBlockIPPosition - start) / 1000f; | |||||
start = start / 1000f; | |||||
// 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).getBounds(); | |||||
rect = new Rectangle2D.Double(rect.getX(), rect.getY() + rect.getHeight(), rect.getWidth(), rect.getHeight()); | |||||
int type = internal ? PDFLink.INTERNAL : PDFLink.EXTERNAL; | |||||
PDFLink pdflink = pdfDoc.makeLink(rect, dest, type); | |||||
currentPage.addAnnotation(pdflink); | |||||
} else { | |||||
super.renderInlineParent(ip); | |||||
} | |||||
} | |||||
public void renderCharacter(Character ch) { | public void renderCharacter(Character ch) { | ||||
super.renderCharacter(ch); | super.renderCharacter(ch); | ||||
if (!textOpen || bl != prevWordY) { | if (!textOpen || bl != prevWordY) { | ||||
closeText(); | closeText(); | ||||
pdf.append("1 0 0 -1 " + (rx / 1000f) + " " + | |||||
(bl / 1000f) + " Tm [" + startText); | |||||
pdf.append("1 0 0 -1 " + (rx / 1000f) + " " | |||||
+ (bl / 1000f) + " Tm [" + startText); | |||||
prevWordY = bl; | prevWordY = bl; | ||||
textOpen = true; | textOpen = true; | ||||
} else { | } else { | ||||
if (emDiff < -33000) { | if (emDiff < -33000) { | ||||
closeText(); | closeText(); | ||||
pdf.append("1 0 0 1 " + (rx / 1000f) + " " + | |||||
(bl / 1000f) + " Tm [" + startText); | |||||
pdf.append("1 0 0 1 " + (rx / 1000f) + " " | |||||
+ (bl / 1000f) + " Tm [" + startText); | |||||
textOpen = true; | textOpen = true; | ||||
} else { | } else { | ||||
pdf.append(Float.toString(emDiff)); | pdf.append(Float.toString(emDiff)); | ||||
(int) uniBytes[i]; | (int) uniBytes[i]; | ||||
String hexString = Integer.toHexString(b); | String hexString = Integer.toHexString(b); | ||||
if (hexString.length() == 1) | |||||
if (hexString.length() == 1) { | |||||
buf = buf.append("0" + hexString); | buf = buf.append("0" + hexString); | ||||
else | |||||
} else { | |||||
buf = buf.append(hexString); | buf = buf.append(hexString); | ||||
} | |||||
} | } | ||||
return buf.toString(); | return buf.toString(); | ||||
} | } | ||||
} | } | ||||
private void updateFont(String name, int size, StringBuffer pdf) { | private void updateFont(String name, int size, StringBuffer pdf) { | ||||
if ((!name.equals(this.currentFontName)) || | |||||
(size != this.currentFontSize)) { | |||||
if ((!name.equals(this.currentFontName)) | |||||
|| (size != this.currentFontSize)) { | |||||
closeText(); | closeText(); | ||||
this.currentFontName = name; | this.currentFontName = name; | ||||
try { | try { | ||||
this.pdfDoc.output(ostream); | this.pdfDoc.output(ostream); | ||||
} catch (IOException ioe) { | } catch (IOException ioe) { | ||||
// ioexception will be caught later | |||||
} | } | ||||
} | } | ||||
protected void placeImage(int x, int y, int w, int h, int xobj) { | protected void placeImage(int x, int y, int w, int h, int xobj) { | ||||
currentStream.add("q\n" + ((float) w) + " 0 0 " + | |||||
((float) - h) + " " + | |||||
(((float) currentBlockIPPosition) / 1000f + x) + " " + | |||||
(((float)(currentBPPosition + 1000 * h)) / 1000f + | |||||
y) + " cm\n" + "/Im" + xobj + " Do\nQ\n"); | |||||
currentStream.add("q\n" + ((float) w) + " 0 0 " | |||||
+ ((float) - h) + " " | |||||
+ (((float) currentBlockIPPosition) / 1000f + x) + " " | |||||
+ (((float)(currentBPPosition + 1000 * h)) / 1000f | |||||
+ y) + " cm\n" + "/Im" + xobj + " Do\nQ\n"); | |||||
} | } | ||||
} | } | ||||
/** | |||||
* Render an inline viewport. | |||||
* This renders an inline viewport by clipping if necessary. | |||||
* @param viewport the viewport to handle | |||||
*/ | |||||
public void renderViewport(Viewport viewport) { | public void renderViewport(Viewport viewport) { | ||||
closeText(); | closeText(); | ||||
currentStream.add("ET\n"); | currentStream.add("ET\n"); | ||||
currentStream.add("BT\n"); | currentStream.add("BT\n"); | ||||
} | } | ||||
/** | |||||
* Render leader area. | |||||
* This renders a leader area which is an area with a rule. | |||||
* @param area the leader area to render | |||||
*/ | |||||
public void renderLeader(Leader area) { | public void renderLeader(Leader area) { | ||||
closeText(); | closeText(); | ||||
currentStream.add("ET\n"); | currentStream.add("ET\n"); | ||||
float startx = ((float) currentBlockIPPosition) / 1000f; | float startx = ((float) currentBlockIPPosition) / 1000f; | ||||
float starty = ((currentBPPosition + area.getOffset()) / 1000f); | float starty = ((currentBPPosition + area.getOffset()) / 1000f); | ||||
float endx = (currentBlockIPPosition + area.getWidth()) / 1000f; | float endx = (currentBlockIPPosition + area.getWidth()) / 1000f; | ||||
if(!alt) { | |||||
if (!alt) { | |||||
currentStream.add(area.getRuleThickness() / 1000f + " w\n"); | currentStream.add(area.getRuleThickness() / 1000f + " w\n"); | ||||
currentStream.add(startx + " " + starty + " m\n"); | currentStream.add(startx + " " + starty + " m\n"); | ||||
currentStream.add(endx + " " + starty + " l\n"); | currentStream.add(endx + " " + starty + " l\n"); | ||||
currentStream.add("S\n"); | currentStream.add("S\n"); | ||||
} else { | } else { | ||||
if(style == RuleStyle.DOUBLE) { | |||||
if (style == RuleStyle.DOUBLE) { | |||||
float third = area.getRuleThickness() / 3000f; | float third = area.getRuleThickness() / 3000f; | ||||
currentStream.add(third + " w\n"); | currentStream.add(third + " w\n"); | ||||
currentStream.add(startx + " " + starty + " m\n"); | currentStream.add(startx + " " + starty + " m\n"); | ||||
currentStream.add(startx + " " + (starty + 2 * half) + " l\n"); | currentStream.add(startx + " " + (starty + 2 * half) + " l\n"); | ||||
currentStream.add("h\n"); | currentStream.add("h\n"); | ||||
currentStream.add("f\n"); | currentStream.add("f\n"); | ||||
if(style == RuleStyle.GROOVE) { | |||||
if (style == RuleStyle.GROOVE) { | |||||
currentStream.add("0 g\n"); | currentStream.add("0 g\n"); | ||||
currentStream.add(startx + " " + starty + " m\n"); | currentStream.add(startx + " " + starty + " m\n"); | ||||
currentStream.add(endx + " " + starty + " l\n"); | currentStream.add(endx + " " + starty + " l\n"); |