package org.apache.fop.apps;
// FOP
+import org.apache.fop.fo.FOUserAgent;
import org.apache.fop.fo.FOTreeBuilder;
import org.apache.fop.fo.ElementMapping;
import org.apache.fop.layout.AreaTree;
_stream = stream;
}
+ public void setUserAgent(FOUserAgent agent) {
+ }
+
+ private FOUserAgent getUserAgent() {
+ return new FOUserAgent();
+ }
+
public void setLogger(Logger logger) {
log = logger;
}
*/
public void setRenderer(Renderer renderer) {
renderer.setLogger(getLogger());
+ renderer.setUserAgent(getUserAgent());
_renderer = renderer;
}
Starter starter = options.getStarter();
starter.run();
} catch (FOPException e) {
- System.err.println("" + e.getMessage());
+ if("null".equals(e.getMessage())) {
+ System.err.println("NullPointerException");
+ } else {
+ System.err.println("" + e.getMessage());
+ }
if (options != null && options.isDebugMode().booleanValue()) {
e.printStackTrace();
+ } else {
+ System.err.println("Turn on debugging for more information");
}
} catch (java.io.FileNotFoundException e) {
System.err.println("" + e.getMessage());
if (options != null && options.isDebugMode().booleanValue()) {
e.printStackTrace();
+ } else {
+ System.err.println("Turn on debugging for more information");
}
}
}
// available markers, markers are discarded once page complete
private ArrayList markers = null;
- boolean blocks = false;
// a block with may contain the dominant styling info in
// terms of most lines or blocks with info
public void addBlock(Block block) {
if (children == null) {
children = new ArrayList();
- } else if (!blocks) {
- // error
}
- blocks = true;
children.add(block);
}
public void addLineArea(LineArea line) {
if (children == null) {
children = new ArrayList();
- } else if (blocks) {
- // error
}
children.add(line);
-
- }
-
- public boolean isChildrenBlocks() {
- return blocks;
}
public int getPositioning() {
public static final int OFFSET = 13;
public static final int SHADOW = 14;
+ public static final int FONT_STATE = 100;
+
public int propType;
public Object data;
TextInfo textInfo;
public static class TextInfo {
- FontState fs;
- float red;
- float green;
- float blue;
- int wrapOption;
- int whiteSpaceCollapse;
- int verticalAlign;
+ public FontState fs;
+ public float red;
+ public float green;
+ public float blue;
+ public int wrapOption;
+ public int whiteSpaceCollapse;
+ public int verticalAlign;
// Textdecoration
- protected boolean underlined = false;
- protected boolean overlined = false;
- protected boolean lineThrough = false;
+ public boolean underlined = false;
+ public boolean overlined = false;
+ public boolean lineThrough = false;
}
TextState ts;
handler.handleXML(ctx, doc, namespace);
} catch (Throwable t) {
// could not handle document
- t.printStackTrace();
+ ctx.getLogger().error("Could not render XML", t);
}
} else {
// no handler found for document
protected void setFlowName(String name) throws FOPException {
if (name == null || name.equals("")) {
- log.warn("A 'flow-name' is required for "
- + getName()
- + ". This constraint will be enforced in future versions of FOP");
- _flowName = "xsl-region-body";
+ throw new FOPException("A 'flow-name' is required for "
+ + getName());
} else {
_flowName = name;
}
-
}
public String getFlowName() {
// FOP
import org.apache.fop.fo.*;
import org.apache.fop.fo.properties.*;
+import org.apache.fop.area.Area;
import org.apache.fop.area.inline.InlineArea;
import org.apache.fop.area.inline.Viewport;
import org.apache.fop.area.inline.ForeignObject;
import org.apache.fop.layout.MarginInlineProps;
import org.apache.fop.layout.RelativePositionProps;
import org.apache.fop.apps.FOPException;
+import org.apache.fop.layoutmgr.LayoutManager;
+import org.apache.fop.layoutmgr.SplitContext;
import org.w3c.dom.Document;
super(parent);
}
+ public LayoutManager getLayoutManager() {
+ return new LayoutManager() {
+ LayoutManager lm;
+ public void generateAreas() {
+ lm.addChild(getInlineArea());
+ }
+
+ public Area getParentArea (Area childArea) {
+ return null;
+ }
+
+ public void addChild (Area childArea) {
+ }
+
+ public boolean splitArea(Area areaToSplit, SplitContext context) {
+ return false;
+ }
+
+ public void setParentLM(LayoutManager lm) {
+ this.lm = lm;
+ }
+
+ };
+ }
+
/**
* Get the inline area created by this element.
*/
// if replaced then use height then ignore block-progression-dimension
//int h = this.properties.get("height").getLength().mvalue();
- // use line-height then ignore dimension in height direction
- int lh = this.properties.get("line-height").getLength().mvalue();
+ // use specified line-height then ignore dimension in height direction
+ boolean hasLH = properties.get("line-height").getSpecifiedValue() != null;
+ int bpd = 0;
+ boolean bpdauto = false;
+ if(hasLH) {
+ bpd = properties.get("line-height").getLength().mvalue();
+ } else {
+ // this property does not apply when the line-height applies
+ // isn't the block-progression-dimension always in the same
+ // direction as the line height?
+ bpdauto = properties.get("block-progression-dimension").getLength().isAuto();
+ bpd = properties.get("block-progression-dimension").getLength().mvalue();
+ }
- int bpd = this.properties.get("block-progression-dimension").getLength().mvalue();
- int ipd = this.properties.get("inline-progression-dimension").getLength().mvalue();
+ //boolean ipdauto = properties.get("inline-progression-dimension").getLength().isAuto();
+ //int ipd = properties.get("inline-progression-dimension").getLength().mvalue();
// if auto then use the intrinsic size of the content scaled
// to the content-height and content-width
// this.hasLines = true;
return 1;
}
-
}
+
}
}
- Status status = new Status(Status.AREA_FULL_NONE);
+ Status status = new Status(Status.OK);
if (null != bestMarker) {
// System.out.println("Laying out marker '" + bestMarker + "' in area '" + area + "'");
// the 'markers' referred to in this method are internal; they have
import org.apache.fop.image.analyser.ImageReader;
import org.apache.fop.configuration.Configuration;
+/*
+handle context: base dir, logger, caching
+
+*/
+
/**
* create FopImage objects (with a configuration file - not yet implemented).
* @author Eric SCHAEFFER
package org.apache.fop.layout;
import java.util.HashMap;
-import java.util.Enumeration;
import org.apache.fop.apps.FOPException;
package org.apache.fop.layoutmgr;
import org.apache.fop.fo.FObj;
+import org.apache.fop.fo.FONode;
import org.apache.fop.area.Area;
import java.util.ListIterator;
public AbstractLayoutManager(FObj fobj) {
- this.fobj = fobj;
- this.parentLM = null;
+ this.fobj = fobj;
+ this.parentLM = null;
}
public void setParentLM(LayoutManager lm) {
- this.parentLM = lm;
+ this.parentLM = lm;
}
* its generateAreas method.
*/
public void generateAreas() {
- ListIterator children = fobj.getChildren();
- while (children.hasNext()) {
- LayoutManager lm = ((FObj)children.next()).getLayoutManager();
- if (lm != null) {
- lm.setParentLM(this);
- lm.generateAreas();
- }
- }
- flush(); // Add last area to parent
+ ListIterator children = fobj.getChildren();
+ while (children.hasNext()) {
+ FONode node = (FONode) children.next();
+ if (node instanceof FObj) {
+ LayoutManager lm = ((FObj) node).getLayoutManager();
+ if (lm != null) {
+ lm.setParentLM(this);
+ lm.generateAreas();
+ }
+ }
+ }
+ flush(); // Add last area to parent
}
-// /**
-// * Ask the parent LayoutManager to add the current (full) area to the
-// * appropriate parent area.
-// * @param bFinished If true, this area is finished, either because it's
-// * completely full or because there is no more content to put in it.
-// * If false, we are in the middle of this area. This can happen,
-// * for example, if we find floats in a line. We stop the current area,
-// * and add it (temporarily) to its parent so that we can see if there
-// * is enough space to place the float(s) anchored in the line.
-// */
-// protected void flush(Area area, boolean bFinished) {
-// if (area != null) {
-// // area.setFinished(true);
-// parentLM.addChild(area, bFinished); // ????
-// if (bFinished) {
-// setCurrentArea(null);
-// }
-// }
-// }
-
- /**
+ // /**
+ // * Ask the parent LayoutManager to add the current (full) area to the
+ // * appropriate parent area.
+ // * @param bFinished If true, this area is finished, either because it's
+ // * completely full or because there is no more content to put in it.
+ // * If false, we are in the middle of this area. This can happen,
+ // * for example, if we find floats in a line. We stop the current area,
+ // * and add it (temporarily) to its parent so that we can see if there
+ // * is enough space to place the float(s) anchored in the line.
+ // */
+ // protected void flush(Area area, boolean bFinished) {
+ // if (area != null) {
+ // // area.setFinished(true);
+ // parentLM.addChild(area, bFinished); // ????
+ // if (bFinished) {
+ // setCurrentArea(null);
+ // }
+ // }
+ // }
+
+ /**
* Force current area to be added to parent area.
*/
abstract protected void flush();
* BPD.
*/
abstract public Area getParentArea(Area childArea);
-
-// public boolean generatesInlineAreas() {
-// return false;
-// }
+
+ // public boolean generatesInlineAreas() {
+ // return false;
+ // }
/**
* dimension of the current area to be exceeded, the parent LM is called
* to add it.
*/
- abstract public void addChild(Area childArea) ;
+ abstract public void addChild(Area childArea);
/** Do nothing */
public boolean splitArea(Area areaToSplit, SplitContext context) {
- context.nextArea = areaToSplit;
- return false;
+ context.nextArea = areaToSplit;
+ return false;
}
-
}
+
private Block curBlockArea;
public BlockLayoutManager(FObj fobj) {
- super(fobj);
+ super(fobj);
}
// DESIGN. Potential alternative to getParentArea() scheme
-// /**
-// * Called by child layout manager to get the available space for
-// * content in the inline progression direction.
-// * Note that a manager may need to ask its parent for this.
-// * For a block area, available IPD is determined by indents.
-// */
-// public int getContentIPD() {
-// getArea(); // make if not existing
-// return blockArea.getIPD();
-// }
+ // /**
+ // * Called by child layout manager to get the available space for
+ // * content in the inline progression direction.
+ // * Note that a manager may need to ask its parent for this.
+ // * For a block area, available IPD is determined by indents.
+ // */
+ // public int getContentIPD() {
+ // getArea(); // make if not existing
+ // return blockArea.getIPD();
+ // }
/**
* Generate areas by tellings all layout managers for its FO's
* children to generate areas.
*/
public void generateAreas() {
- ListIterator children = fobj.getChildren();
- LayoutManager lm=null;
- while (children.hasNext()) {
- FObj childFO = (FObj)children.next();
- if (childFO.generatesInlineAreas()) {
- children.previous();
- lm = new LineLayoutManager(children);
- }
- else {
- lm = childFO.getLayoutManager();
- }
- if (lm != null) {
- lm.setParentLM(this);
- lm.generateAreas();
- }
- }
- flush(); // Add last area to parent
+ ListIterator children = fobj.getChildren();
+ LayoutManager lm = null;
+ while (children.hasNext()) {
+ FObj childFO = (FObj) children.next();
+ if (childFO.generatesInlineAreas()) {
+ children.previous();
+ lm = new LineLayoutManager(children);
+ } else {
+ lm = childFO.getLayoutManager();
+ }
+ if (lm != null) {
+ lm.setParentLM(this);
+ lm.generateAreas();
+ }
+ }
+ flush(); // Add last area to parent
}
* BPD.
*/
public Area getParentArea(Area childArea) {
- if (curBlockArea == null) {
- curBlockArea = new Block();
- // Set up dimensions
- // Must get dimensions from parent area
- //MinOptMax referenceIPD = parentLM.getReferenceIPD();
- Area parentArea = parentLM.getParentArea(curBlockArea);
- // Get reference IPD from parentArea
- setCurrentArea(curBlockArea); // ??? for generic operations
- }
- return curBlockArea;
+ if (curBlockArea == null) {
+ curBlockArea = new Block();
+ // Set up dimensions
+ // Must get dimensions from parent area
+ //MinOptMax referenceIPD = parentLM.getReferenceIPD();
+ Area parentArea = parentLM.getParentArea(curBlockArea);
+ // Get reference IPD from parentArea
+ setCurrentArea(curBlockArea); // ??? for generic operations
+ }
+ return curBlockArea;
}
-
+
public void addChild(Area childArea) {
- if (curBlockArea != null) {
- if (childArea instanceof LineArea) {
- // Something about widows and orphans
- // Position the line area and calculate size...
- curBlockArea.addLineArea((LineArea)childArea);
- }
- else {
- super.addChild(childArea);
- }
- }
+ if (curBlockArea != null) {
+ if (childArea instanceof LineArea) {
+ // Something about widows and orphans
+ // Position the line area and calculate size...
+ curBlockArea.addLineArea((LineArea) childArea);
+ } else {
+ super.addChild(childArea);
+ }
+ }
}
-// /**
-// * Called by child LayoutManager when it has filled one of its areas.
-// * If no current container, make one.
-// * See if the area will fit in the current container.
-// * If so, add it.
-// * @param childArea the area to add: will either be a LineArea or
-// * a BlockArea.
-// */
-// public void addChild(Area childArea) {
-// /* If the childArea fits entirely in the maximum available BPD
-// * add it and return an OK status.
-// * If it doesn't all fit, overrun or ask for split?
-// * Might as well just add it since the page layout process
-// * may need to make other adjustments, resulting in changing
-// * split point.
-// */
-// // Things like breaks on child area can cause premature
-// // termination of the current area.
-// /* We go past the theoretical maximum to be able to handle things
-// * like widows.
-// */
-// // WARNING: this doesn't take into account space-specifier
-// // adujstment between childArea and last content of blockArea!
-// if (blockArea.getContentBPD().min + childArea.getAllocationBPD().min
-// > blockArea.getAvailBPD().max) {
-// if (++extraLines <= iWidows) {
-// blockArea.add(childArea);
-// }
-// else {
-// blockArea.setIsLast(false);
-// parentLM.addChildArea(blockArea);
-// // Make a new one for this area
-// blockArea = makeAreaForChild(childArea);
-// extraLines = 0; // Count potential widows
-// blockArea.add(childArea);
-// }
-// }
-// else {
-// blockArea.add(childArea);
-// }
-// }
+ // /**
+ // * Called by child LayoutManager when it has filled one of its areas.
+ // * If no current container, make one.
+ // * See if the area will fit in the current container.
+ // * If so, add it.
+ // * @param childArea the area to add: will either be a LineArea or
+ // * a BlockArea.
+ // */
+ // public void addChild(Area childArea) {
+ // /* If the childArea fits entirely in the maximum available BPD
+ // * add it and return an OK status.
+ // * If it doesn't all fit, overrun or ask for split?
+ // * Might as well just add it since the page layout process
+ // * may need to make other adjustments, resulting in changing
+ // * split point.
+ // */
+ // // Things like breaks on child area can cause premature
+ // // termination of the current area.
+ // /* We go past the theoretical maximum to be able to handle things
+ // * like widows.
+ // */
+ // // WARNING: this doesn't take into account space-specifier
+ // // adujstment between childArea and last content of blockArea!
+ // if (blockArea.getContentBPD().min + childArea.getAllocationBPD().min
+ // > blockArea.getAvailBPD().max) {
+ // if (++extraLines <= iWidows) {
+ // blockArea.add(childArea);
+ // }
+ // else {
+ // blockArea.setIsLast(false);
+ // parentLM.addChildArea(blockArea);
+ // // Make a new one for this area
+ // blockArea = makeAreaForChild(childArea);
+ // extraLines = 0; // Count potential widows
+ // blockArea.add(childArea);
+ // }
+ // }
+ // else {
+ // blockArea.add(childArea);
+ // }
+ // }
}
* Base LayoutManager class for all areas which stack their child
* areas in the block-progression direction, such as Flow, Block, ListBlock.
*/
-public abstract class BlockStackingLayoutManager
- extends AbstractLayoutManager {
+public abstract class BlockStackingLayoutManager extends AbstractLayoutManager {
/** Reference to FO whose areas it's managing or to the traits
* of the FO.
*/
BlockParent parentArea = null;
public BlockStackingLayoutManager(FObj fobj) {
- super(fobj);
+ super(fobj);
}
public boolean splitArea(Area area, SplitContext splitContext) {
- // Divide area so that it will be within targetLength if possible
- // If not, it can be shorter, but not longer.
- /* Iterate over contents of the area. */
-
- // Need to figure out if we can do this generically
- // Logically a BlockStacking LM only handles Block-type areas
- if (!(area instanceof BlockParent)) {
- return false;
- }
- Iterator areaIter = ((BlockParent)area).getChildAreas().iterator();
-
-
- BreakCost minBreakCost = null;
- MinOptMax remainBPD = splitContext.targetBPD;
- splitContext.nextArea = area;
-
- while (areaIter.hasNext()) {
- Area childArea = (Area)areaIter.next();
- if (remainBPD.max < childArea.getAllocationBPD().min) {
- // Past the end point: try to break it
- // TODO: get a LayoutManager to do the split of the child
- // area, either Area => LM or Area => gen FO => LM
- LayoutManager childLM = childArea.getGeneratingFObj().
- getLayoutManager();
- splitContext.targetBPD = remainBPD;
- if (childLM.splitArea(childArea, splitContext) == false) {
- // Can't split, so must split this area before childArea
- // Can we pass the iter?
- // If already saw several a potential break, use it
- if (minBreakCost != null) {
- /* Split 'area', placing all children after
- * minBreakCost.getArea() into a new area,
- * which we store in the splitContext.
- */
- // splitContext.nextArea = area.splitAfter(minBreakCost.getArea());
- }
- else {
- /* This area will be shorter than the desired minimum.
- * Split before the current childArea (which will be
- * the first area in the newly created Area.
- */
- //splitContext.nextArea = area.splitBefore(childArea);
- }
- }
- else return true; // childLM has done the work for us!
- // Set cost, dimension ???
- break;
- }
- else {
- remainBPD.subtract(childArea.getAllocationBPD());
- if (remainBPD.min < 0) {
- // Potential breakpoint: remember break Position and
- // break "cost" (constraint violation)
- BreakCost breakCost = evaluateBreakCost(area, childArea);
- minBreakCost = breakCost.chooseLowest(minBreakCost);
- }
- }
- //Note: size of area when split can depend on conditional
- // space, border and padding of the split area!!!
- }
- // True if some part of area can be placed, false if none is placed
- return (splitContext.nextArea != area);
-
+ // Divide area so that it will be within targetLength if possible
+ // If not, it can be shorter, but not longer.
+ /* Iterate over contents of the area. */
+
+ // Need to figure out if we can do this generically
+ // Logically a BlockStacking LM only handles Block-type areas
+ if (!(area instanceof BlockParent)) {
+ return false;
+ }
+ Iterator areaIter = ((BlockParent) area).getChildAreas().iterator();
+
+
+ BreakCost minBreakCost = null;
+ MinOptMax remainBPD = splitContext.targetBPD;
+ splitContext.nextArea = area;
+
+ while (areaIter.hasNext()) {
+ Area childArea = (Area) areaIter.next();
+ if (remainBPD.max < childArea.getAllocationBPD().min) {
+ // Past the end point: try to break it
+ // TODO: get a LayoutManager to do the split of the child
+ // area, either Area => LM or Area => gen FO => LM
+ LayoutManager childLM =
+ childArea.getGeneratingFObj(). getLayoutManager();
+ splitContext.targetBPD = remainBPD;
+ if (childLM.splitArea(childArea, splitContext) == false) {
+ // Can't split, so must split this area before childArea
+ // Can we pass the iter?
+ // If already saw several a potential break, use it
+ if (minBreakCost != null) {
+ /* Split 'area', placing all children after
+ * minBreakCost.getArea() into a new area,
+ * which we store in the splitContext.
+ */
+ // splitContext.nextArea = area.splitAfter(minBreakCost.getArea());
+ } else {
+ /* This area will be shorter than the desired minimum.
+ * Split before the current childArea (which will be
+ * the first area in the newly created Area.
+ */
+ //splitContext.nextArea = area.splitBefore(childArea);
+ }
+ } else
+ return true; // childLM has done the work for us!
+ // Set cost, dimension ???
+ break;
+ } else {
+ remainBPD.subtract(childArea.getAllocationBPD());
+ if (remainBPD.min < 0) {
+ // Potential breakpoint: remember break Position and
+ // break "cost" (constraint violation)
+ BreakCost breakCost =
+ evaluateBreakCost(area, childArea);
+ minBreakCost = breakCost.chooseLowest(minBreakCost);
+ }
+ }
+ //Note: size of area when split can depend on conditional
+ // space, border and padding of the split area!!!
+ }
+ // True if some part of area can be placed, false if none is placed
+ return (splitContext.nextArea != area);
+
}
private BreakCost evaluateBreakCost(Area parent, Area child) {
- return new BreakCost(child,0);
+ return new BreakCost(child, 0);
}
- /** return current area being filled
+ /** return current area being filled
*/
protected BlockParent getCurrentArea() {
- return this.parentArea;
+ return this.parentArea;
}
- /**
+ /**
* Set the current area being filled.
*/
protected void setCurrentArea(BlockParent parentArea) {
- this.parentArea = parentArea;
+ this.parentArea = parentArea;
}
protected MinOptMax resolveSpaceSpecifier(Area nextArea) {
- SpaceSpecifier spaceSpec = new SpaceSpecifier();
-// Area prevArea = getCurrentArea().getLast();
-// if (prevArea != null) {
-// spaceSpec.addSpace(prevArea.getSpaceAfter());
-// }
-// spaceSpec.addSpace(nextArea.getSpaceBefore());
- return spaceSpec.resolve();
+ SpaceSpecifier spaceSpec = new SpaceSpecifier();
+ // Area prevArea = getCurrentArea().getLast();
+ // if (prevArea != null) {
+ // spaceSpec.addSpace(prevArea.getSpaceAfter());
+ // }
+ // spaceSpec.addSpace(nextArea.getSpaceBefore());
+ return spaceSpec.resolve();
}
/**
* @param parentArea the area in which to add the childArea
*/
protected void addChildToArea(Area childArea, BlockParent parentArea) {
- // This should be a block-level Area (Block in the generic sense)
- if (!(childArea instanceof Block)) {
- System.err.println("Child not a Block in BlockStackingLM!");
- return;
- }
-
- // See if the whole thing fits, including space before
- // Calculate space between last child in curFlow and childArea
- MinOptMax targetDim = parentArea.getAvailBPD();
- MinOptMax spaceBefore = resolveSpaceSpecifier(childArea) ;
- targetDim.subtract(spaceBefore);
- if (targetDim.max >= childArea.getAllocationBPD().min) {
- //parentArea.addBlock(new InterBlockSpace(spaceBefore));
- parentArea.addBlock((Block)childArea);
- return;
- }
- else {
- // Probably need something like max BPD so we don't get into
- // infinite loops with large unbreakable chunks
- SplitContext splitContext = new SplitContext(targetDim);
-
- LayoutManager childLM = childArea.getGeneratingFObj().
- getLayoutManager();
- if (childLM.splitArea(childArea, splitContext)) {
- //parentArea.addBlock(new InterBlockSpace(spaceBefore));
- parentArea.addBlock((Block)childArea);
- }
- flush(); // hand off current area to parent
- getParentArea(splitContext.nextArea);
- // Check that reference IPD hasn't changed!!!
- // If it has, we must "reflow" the content
- addChild(splitContext.nextArea);
- }
+ // This should be a block-level Area (Block in the generic sense)
+ if (!(childArea instanceof Block)) {
+ System.err.println("Child not a Block in BlockStackingLM!");
+ return;
+ }
+
+ // See if the whole thing fits, including space before
+ // Calculate space between last child in curFlow and childArea
+ MinOptMax targetDim = parentArea.getAvailBPD();
+ MinOptMax spaceBefore = resolveSpaceSpecifier(childArea);
+ targetDim.subtract(spaceBefore);
+ if (targetDim.max >= childArea.getAllocationBPD().min) {
+ //parentArea.addBlock(new InterBlockSpace(spaceBefore));
+ parentArea.addBlock((Block) childArea);
+ return;
+ } else {
+ // Probably need something like max BPD so we don't get into
+ // infinite loops with large unbreakable chunks
+ SplitContext splitContext = new SplitContext(targetDim);
+
+ LayoutManager childLM =
+ childArea.getGeneratingFObj(). getLayoutManager();
+ if (childLM.splitArea(childArea, splitContext)) {
+ //parentArea.addBlock(new InterBlockSpace(spaceBefore));
+ parentArea.addBlock((Block) childArea);
+ }
+ flush(); // hand off current area to parent
+ getParentArea(splitContext.nextArea);
+ // Check that reference IPD hasn't changed!!!
+ // If it has, we must "reflow" the content
+ addChild(splitContext.nextArea);
+ }
}
* @param childArea the area to add: will be some block-stacked Area.
*/
public void addChild(Area childArea) {
- addChildToArea(childArea, getCurrentArea());
+ addChildToArea(childArea, getCurrentArea());
}
- /**
+ /**
* Force current area to be added to parent area.
*/
protected void flush() {
- parentLM.addChild(getCurrentArea());
+ if (getCurrentArea() != null)
+ parentLM.addChild(getCurrentArea());
}
private int cost; // Will be more complicated than this!
public BreakCost(Area breakArea, int cost) {
- this.breakArea = breakArea;
- this.cost = cost;
+ this.breakArea = breakArea;
+ this.cost = cost;
}
Area getArea() {
- return breakArea;
+ return breakArea;
}
public BreakCost chooseLowest(BreakCost otherCost) {
- return this;
+ return this;
}
}
* It is created by the PageSequence FO.
*/
public FlowLayoutManager(FObj fobj) {
- super(fobj);
+ super(fobj);
}
* at any one time. The actual work is done by BlockStackingLM.
*/
public void addChild(Area childArea) {
- addChildToArea(childArea,
- this.currentAreas[childArea.getAreaClass()]);
+ addChildToArea(childArea,
+ this.currentAreas[childArea.getAreaClass()]);
}
public Area getParentArea(Area childArea) {
- // Get an area from the Page
- BlockParent parentArea =
- (BlockParent)parentLM.getParentArea(childArea);
- this.currentAreas[parentArea.getAreaClass()] = parentArea;
- setCurrentArea(parentArea);
- return parentArea;
+ // Get an area from the Page
+ BlockParent parentArea =
+ (BlockParent) parentLM.getParentArea(childArea);
+ this.currentAreas[parentArea.getAreaClass()] = parentArea;
+ setCurrentArea(parentArea);
+ return parentArea;
}
private InlineArea curArea;
public LeafNodeLayoutManager(FObj fobj) {
- super(fobj);
+ super(fobj);
}
protected void setCurrentArea(InlineArea ia) {
- curArea = ia;
+ curArea = ia;
}
protected void flush() {
- parentLM.addChild(curArea);
+ parentLM.addChild(curArea);
}
/**
* This is a leaf-node, so this method is never called.
*/
public Area getParentArea(Area childArea) {
- return null;
+ return null;
}
-
+
}
private MinOptMax remainingIPD;
public LineLayoutManager(ListIterator fobjIter) {
- super(null);
- this.fobjIter = fobjIter;
+ super(null);
+ this.fobjIter = fobjIter;
}
-
/**
* Call child layout managers to generate content as long as they
* generate inline areas. If a block-level generating LM is found,
* finish any line being filled and return to the parent LM.
*/
public void generateAreas() {
- this.bFirstLine = true;
- while (fobjIter.hasNext()) {
- FObj childFO = (FObj)fobjIter.next();
- if (childFO.generatesInlineAreas()==false) {
- // It generates blocks, pass back to parent
- // Back up one
- fobjIter.previous();
- break;
- }
- else { // generates inline area
- curLM = childFO.getLayoutManager();
- if (curLM != null) {
- curLM.setParentLM(this);
- curLM.generateAreas();
- }
- }
- }
- flush(); // Add last area to parent
+ this.bFirstLine = true;
+ while (fobjIter.hasNext()) {
+ FObj childFO = (FObj) fobjIter.next();
+ if (childFO.generatesInlineAreas() == false) {
+ // It generates blocks, pass back to parent
+ // Back up one
+ fobjIter.previous();
+ break;
+ } else { // generates inline area
+ curLM = childFO.getLayoutManager();
+ if (curLM != null) {
+ curLM.setParentLM(this);
+ curLM.generateAreas();
+ }
+ }
+ }
+ flush(); // Add last area to parent
}
-
/**
* Align and position curLine and add it to parentContainer.
* Set curLine to null.
*/
public void flush() {
- if (lineArea != null) {
- // Adjust spacing as necessary
- // Calculate height, based on content (or does the Area do this?)
- parentLM.addChild(lineArea);
- lineArea = null;
- }
+ if (lineArea != null) {
+ // Adjust spacing as necessary
+ // Calculate height, based on content (or does the Area do this?)
+ lineArea.setHeight(14000);
+ parentLM.addChild(lineArea);
+ lineArea = null;
+ }
}
-
/**
* Return current lineArea or generate a new one if necessary.
*/
public Area getParentArea(Area childArea) {
- if (lineArea == null) {
- createLine();
- }
- return lineArea;
+ if (lineArea == null) {
+ createLine();
+ }
+ return lineArea;
}
private void createLine() {
- lineArea = new LineArea();
- /* Set line IPD from parentArea
- * This accounts for indents. What about first line indent?
- * Should we set an "isFirst" flag on the lineArea to signal
- * that to the parent (Block) LM? That's where indent property
- * information will be managed.
- */
- Area parent = parentLM.getParentArea(lineArea);
- // lineArea.setContentIPD(parent.getContentIPD());
- // remainingIPD = parent.getContentIPD();
- // OR???
- // remainingIPD = parentLM.getContentIPD();
- remainingIPD = new MinOptMax(100000); // TESTING!!!
- this.bFirstLine = false;
+ lineArea = new LineArea();
+ /* Set line IPD from parentArea
+ * This accounts for indents. What about first line indent?
+ * Should we set an "isFirst" flag on the lineArea to signal
+ * that to the parent (Block) LM? That's where indent property
+ * information will be managed.
+ */
+ Area parent = parentLM.getParentArea(lineArea);
+ // lineArea.setContentIPD(parent.getContentIPD());
+ // remainingIPD = parent.getContentIPD();
+ // OR???
+ // remainingIPD = parentLM.getContentIPD();
+ remainingIPD = new MinOptMax(300000); // TESTING!!!
+ this.bFirstLine = false;
}
-
/**
* Called by child LayoutManager when it has filled one of its areas.
* See if the area will fit in the current container.
* @param childArea the area to add: should be an InlineArea subclass!
*/
public void addChild(Area childArea) {
- if ((childArea instanceof InlineArea)==false) {
- // SIGNAL AN ERROR!!!
- return;
- }
- InlineArea inlineArea = (InlineArea)childArea;
- if (lineArea == null) {
- createLine();
- }
- if (inlineArea.getAllocationIPD().min < remainingIPD.max) {
- lineArea.addInlineArea(inlineArea);
- remainingIPD.subtract(inlineArea.getAllocationIPD());
- // Calculate number of spaces
- // Forced line break after this area (ex. ends with LF in nowrap)
- /* NOTYET!
- if (inlineArea.breakAfter()) {
- flush();
- }
- */
- /* Check if line could end after this area (potential line-break
- * character. If not, it must be joined with following inline
- * area to make a word. Otherwise, if the line could break here
- * and if it is "full", add it to the parent area.
- */
- if (remainingIPD.min<=0) {
- flush();
- }
- }
-
- else {
- /* The inline area won't entirely fit in this line. Ask its
- * layout manager to split it (by hyphenation for example),
- * in order to fit part of it in the line.
- * Note: only the current child LM could have generated this
- * area, so we ask it to do the split.
- */
- SplitContext splitContext = new SplitContext(remainingIPD);
- if (curLM.splitArea(inlineArea, splitContext)) {
- // inlineArea should now fit
- lineArea.addInlineArea(inlineArea);
- flush();
- }
- addChild(splitContext.nextArea);
- }
+ if ((childArea instanceof InlineArea) == false) {
+ // SIGNAL AN ERROR!!!
+ return;
+ }
+ InlineArea inlineArea = (InlineArea) childArea;
+ if (lineArea == null) {
+ createLine();
+ }
+ if (inlineArea.getAllocationIPD().min < remainingIPD.max) {
+ lineArea.addInlineArea(inlineArea);
+ remainingIPD.subtract(inlineArea.getAllocationIPD());
+ // Calculate number of spaces
+ // Forced line break after this area (ex. ends with LF in nowrap)
+ /* NOTYET!
+ if (inlineArea.breakAfter()) {
+ flush();
+ }
+ */
+ /* Check if line could end after this area (potential line-break
+ * character. If not, it must be joined with following inline
+ * area to make a word. Otherwise, if the line could break here
+ * and if it is "full", add it to the parent area.
+ */
+ if (remainingIPD.min <= 0) {
+ flush();
+ }
+ }
+ else {
+ /* The inline area won't entirely fit in this line. Ask its
+ * layout manager to split it (by hyphenation for example),
+ * in order to fit part of it in the line.
+ * Note: only the current child LM could have generated this
+ * area, so we ask it to do the split.
+ */
+ SplitContext splitContext = new SplitContext(remainingIPD);
+ if (curLM.splitArea(inlineArea, splitContext)) {
+ // inlineArea should now fit
+ lineArea.addInlineArea(inlineArea);
+ flush();
+ addChild(splitContext.nextArea);
+ } else {
+ lineArea.addInlineArea((InlineArea)splitContext.nextArea);
+ remainingIPD.subtract(inlineArea.getAllocationIPD());
+ if (remainingIPD.min <= 0) {
+ flush();
+ }
+ }
+ }
}
}
* LayoutManager for a PageSequence and its flow.
* It manages all page-related layout.
*/
-public class PageLayoutManager extends AbstractLayoutManager
- implements Runnable{
+public class PageLayoutManager extends AbstractLayoutManager implements Runnable {
/** True if haven't yet laid out any pages.*/
private boolean bFirstPage;
* It is created by the PageSequence FO.
*/
public PageLayoutManager(AreaTree areaTree, PageSequence pageseq) {
- super( pageseq);
- this.areaTree = areaTree;
+ super(pageseq);
+ this.areaTree = areaTree;
}
- /**
+ /**
* The layout process is designed to be able to be run in a thread.
* In theory it can run at the same
* time as FO tree generation, once the layout-master-set has been read.
* rendering process can also run in a parallel thread.
*/
public void run() {
- generateAreas();
+ generateAreas();
}
* For now, only handle normal flow areas.
*/
public void addChild(Area childArea) {
- if (childArea ==null) return;
- if (childArea.getAreaClass() == Area.CLASS_NORMAL) {
- placeFlowRefArea(childArea);
- }
- else ; // TODO: all the others!
+ if (childArea == null)
+ return;
+ if (childArea.getAreaClass() == Area.CLASS_NORMAL) {
+ placeFlowRefArea(childArea);
+ } else
+ ; // TODO: all the others!
}
/**
* possibly moving to the next column or to the next page.
*/
protected void placeFlowRefArea(Area area) {
- // assert (curSpan != null);
- // assert (area == curFlow);
- // assert (curFlow == curSpan.getFlow(curSpan.getColumnCount()-1));
- // assert (area.getBPD().min < curSpan.getHeight());
- // Last column on this page is filled
- // See if the flow is full. The Flow LM can add an area before
- // it's full in the case of a break or a span.
- // Also in the case of a float to be placed. In that case, there
- // may be further material added later.
- // The Flow LM sets the "finished" flag on the Flow Area if it has
- // completely filled it. In this case, if on the last column
- // end the page.
-
- // Alternatively the child LM indicates to parent that it's full?
- if (area.getAllocationBPD().max >= curSpan.getMaxBPD().min) {
- // Consider it filled
- if (curSpan.getColumnCount() == curSpanColumns) {
- finishPage();
- }
- else curFlow = null; // Create new flow on next getParentArea()
- }
+ // assert (curSpan != null);
+ // assert (area == curFlow);
+ // assert (curFlow == curSpan.getFlow(curSpan.getColumnCount()-1));
+ // assert (area.getBPD().min < curSpan.getHeight());
+ // Last column on this page is filled
+ // See if the flow is full. The Flow LM can add an area before
+ // it's full in the case of a break or a span.
+ // Also in the case of a float to be placed. In that case, there
+ // may be further material added later.
+ // The Flow LM sets the "finished" flag on the Flow Area if it has
+ // completely filled it. In this case, if on the last column
+ // end the page.
+
+ // Alternatively the child LM indicates to parent that it's full?
+ if (area.getAllocationBPD().max >= curSpan.getMaxBPD().min) {
+ // Consider it filled
+ if (curSpan.getColumnCount() == curSpanColumns) {
+ finishPage();
+ } else
+ curFlow = null; // Create new flow on next getParentArea()
+ }
}
}
protected void placeFootnote(Area area) {
- // After doing this, reduce available space on the curSpan.
- // This has to be propagated to the curFlow (FlowLM) so that
- // it can adjust its limit for composition (or it just asks
- // curSpan for BPD before doing the break?)
- // If multi-column, we may have to balance to find more space
- // for a float. When?
+ // After doing this, reduce available space on the curSpan.
+ // This has to be propagated to the curFlow (FlowLM) so that
+ // it can adjust its limit for composition (or it just asks
+ // curSpan for BPD before doing the break?)
+ // If multi-column, we may have to balance to find more space
+ // for a float. When?
}
- private PageViewport makeNewPage(boolean bIsBlank, boolean bIsLast) {
- finishPage();
- try {
- curPage = ((PageSequence)fobj).createPage(bIsBlank, bIsLast);
- } catch (FOPException fopex) { /* ???? */ }
- curBody = (BodyRegion) curPage.getPage().
- getRegion(RegionReference.BODY).getRegion();
- return curPage;
- }
+ private PageViewport makeNewPage(boolean bIsBlank, boolean bIsLast) {
+ finishPage();
+ try {
+ curPage = ((PageSequence) fobj).createPage(bIsBlank, bIsLast);
+ } catch (FOPException fopex) { /* ???? */
+ }
+ curBody = (BodyRegion) curPage.getPage(). getRegion(
+ RegionReference.BODY).getRegion();
+ return curPage;
+ }
private void finishPage() {
- if (curPage != null) {
- // Layout static content into the regions
- // Need help from pageseq for this
- // Queue for ID resolution and rendering
- areaTree.addPage(curPage);
- curPage = null;
- curBody = null;
- curSpan=null;
- curFlow=null;
- }
+ if (curPage != null) {
+ // Layout static content into the regions
+ // Need help from pageseq for this
+ // Queue for ID resolution and rendering
+ areaTree.addPage(curPage);
+ curPage = null;
+ curBody = null;
+ curSpan = null;
+ curFlow = null;
+ }
}
/**
* and span properties set.
*/
public Area getParentArea(Area childArea) {
- int aclass = childArea.getAreaClass() ;
- if (aclass == Area.CLASS_NORMAL) {
- // TODO: how to get properties from the Area???
- // Need span, break
- int breakVal = Constants.AUTO; // childArea.getBreakBefore();
- if (breakVal != Constants.AUTO) {
- // We may be forced to make new page
- handleBreak(breakVal);
- }
- else if (curPage == null) {
- makeNewPage(false, false);
- }
- // Now we should be on the right kind of page
- boolean bNeedSpan = false;
- int span = Constants.NONE; // childArea.getSpan()
- int numCols=1;
- if (span == Constants.ALL) {
- // Assume the number of columns is stored on the curBody object.
- //numCols = curBody.getProperty(NUMBER_OF_COLUMNS);
- }
- if (curSpan == null) {
- createBodyMainReferenceArea();
- bNeedSpan = true;
- }
- else if (numCols != curSpanColumns) {
- // TODO: BALANCE EXISTING COLUMNS
- if (curSpanColumns > 1) {
- // balanceColumns();
- }
- bNeedSpan = true;
- }
- if (bNeedSpan) {
- // Make a new span and the first flow
- createSpan(numCols);
- }
- else if (curFlow == null) {
- createFlow();
- }
- return curFlow;
- }
- else {
- if (curPage == null) {
- makeNewPage(false, false);
- }
- // Now handle different kinds of areas
- if (aclass == Area.CLASS_BEFORE_FLOAT) {
- BeforeFloat bf = curBody.getBeforeFloat();
- if (bf == null) {
- bf = new BeforeFloat();
- curBody.setBeforeFloat(bf);
- }
- return bf;
- }
- else if (aclass == Area.CLASS_FOOTNOTE) {
- Footnote fn = curBody.getFootnote();
- if (fn == null) {
- fn = new Footnote();
- curBody.setFootnote(fn);
- }
- return fn;
- }
- // TODO!!! other area classes (side-float, absolute, fixed)
- return null;
- }
+ int aclass = childArea.getAreaClass();
+ if (aclass == Area.CLASS_NORMAL) {
+ // TODO: how to get properties from the Area???
+ // Need span, break
+ int breakVal = Constants.AUTO; // childArea.getBreakBefore();
+ if (breakVal != Constants.AUTO) {
+ // We may be forced to make new page
+ handleBreak(breakVal);
+ } else if (curPage == null) {
+ makeNewPage(false, false);
+ }
+ // Now we should be on the right kind of page
+ boolean bNeedSpan = false;
+ int span = Constants.NONE; // childArea.getSpan()
+ int numCols = 1;
+ if (span == Constants.ALL) {
+ // Assume the number of columns is stored on the curBody object.
+ //numCols = curBody.getProperty(NUMBER_OF_COLUMNS);
+ }
+ if (curSpan == null) {
+ createBodyMainReferenceArea();
+ bNeedSpan = true;
+ } else if (numCols != curSpanColumns) {
+ // TODO: BALANCE EXISTING COLUMNS
+ if (curSpanColumns > 1) {
+ // balanceColumns();
+ }
+ bNeedSpan = true;
+ }
+ if (bNeedSpan) {
+ // Make a new span and the first flow
+ createSpan(numCols);
+ } else if (curFlow == null) {
+ createFlow();
+ }
+ return curFlow;
+ } else {
+ if (curPage == null) {
+ makeNewPage(false, false);
+ }
+ // Now handle different kinds of areas
+ if (aclass == Area.CLASS_BEFORE_FLOAT) {
+ BeforeFloat bf = curBody.getBeforeFloat();
+ if (bf == null) {
+ bf = new BeforeFloat();
+ curBody.setBeforeFloat(bf);
+ }
+ return bf;
+ } else if (aclass == Area.CLASS_FOOTNOTE) {
+ Footnote fn = curBody.getFootnote();
+ if (fn == null) {
+ fn = new Footnote();
+ curBody.setFootnote(fn);
+ }
+ return fn;
+ }
+ // TODO!!! other area classes (side-float, absolute, fixed)
+ return null;
+ }
}
* not have the desired "handedness".
*/
protected void handleBreak(int breakVal) {
- if (breakVal == Constants.COLUMN) {
- if (curSpan != null &&
- curSpan.getColumnCount() != curSpanColumns) {
- // Move to next column
- createFlow();
- return;
- }
- // else need new page
- breakVal = Constants.PAGE;
- }
- if (needEmptyPage(breakVal)) {
- curPage = makeNewPage(true, false);
- }
- if (needNewPage(breakVal)) {
- curPage = makeNewPage(false, false);
- }
+ if (breakVal == Constants.COLUMN) {
+ if (curSpan != null &&
+ curSpan.getColumnCount() != curSpanColumns) {
+ // Move to next column
+ createFlow();
+ return;
+ }
+ // else need new page
+ breakVal = Constants.PAGE;
+ }
+ if (needEmptyPage(breakVal)) {
+ curPage = makeNewPage(true, false);
+ }
+ if (needNewPage(breakVal)) {
+ curPage = makeNewPage(false, false);
+ }
}
* block until the queue of layoutable stuff is empty!
*/
private boolean needEmptyPage(int breakValue) {
- return false;
-// if (breakValue == Constants.PAGE || curPage.isEmpty()) {
-// // any page is OK or we already have an empty page
-// return false;
-// }
-// else {
-// /* IF we are on the kind of page we need, we'll need a new page. */
-// if (curPage.getPageNumber()%2 != 0) {
-// // Current page is odd
-// return (breakValue == Constants.ODD_PAGE);
-// }
-// else {
-// return (breakValue == Constants.EVEN_PAGE);
-// }
-// }
+ return false;
+ // if (breakValue == Constants.PAGE || curPage.isEmpty()) {
+ // // any page is OK or we already have an empty page
+ // return false;
+ // }
+ // else {
+ // /* IF we are on the kind of page we need, we'll need a new page. */
+ // if (curPage.getPageNumber()%2 != 0) {
+ // // Current page is odd
+ // return (breakValue == Constants.ODD_PAGE);
+ // }
+ // else {
+ // return (breakValue == Constants.EVEN_PAGE);
+ // }
+ // }
}
-
+
/**
* See if need to generate a new page for a forced break condition.
* TODO: methods to see if the current page is empty and to get
* its number.
*/
private boolean needNewPage(int breakValue) {
- return false;
-// if (curPage.isEmpty()) {
-// if (breakValue == Constants.PAGE) {
-// return false;
-// }
-// else if (curPage.getPageNumber()%2 != 0) {
-// // Current page is odd
-// return (breakValue == Constants.EVEN_PAGE);
-// }
-// else {
-// return (breakValue == Constants.ODD_PAGE);
-// }
-// }
-// else {
-// return true;
-// }
+ return false;
+ // if (curPage.isEmpty()) {
+ // if (breakValue == Constants.PAGE) {
+ // return false;
+ // }
+ // else if (curPage.getPageNumber()%2 != 0) {
+ // // Current page is odd
+ // return (breakValue == Constants.EVEN_PAGE);
+ // }
+ // else {
+ // return (breakValue == Constants.ODD_PAGE);
+ // }
+ // }
+ // else {
+ // return true;
+ // }
}
private void createBodyMainReferenceArea() {
- curBody.setMainReference(new MainReference());
+ curBody.setMainReference(new MainReference());
}
private Flow createFlow() {
- curFlow = new Flow();
- // Set IPD and max BPD on the curFlow from curBody
- curSpan.addFlow(curFlow);
- return curFlow;
+ curFlow = new Flow();
+ // Set IPD and max BPD on the curFlow from curBody
+ curSpan.addFlow(curFlow);
+ return curFlow;
}
private void createSpan(int numCols) {
- // check number of columns (= all in Body or 1)
- // If already have a span, get its size and position (as MinMaxOpt)
- // This determines the position of the new span area
- // Attention: space calculation between the span areas.
-
-// MinOptMax newpos ;
-// if (curSpan != null) {
-// newpos = curSpan.getPosition(BPD);
-// newpos.add(curSpan.getDimension(BPD));
-// }
-// else newpos = new MinOptMax();
- curSpan = new Span(numCols);
- //curSpan.setPosition(BPD, newpos);
- curBody.getMainReference().addSpan(curSpan);
- createFlow();
+ // check number of columns (= all in Body or 1)
+ // If already have a span, get its size and position (as MinMaxOpt)
+ // This determines the position of the new span area
+ // Attention: space calculation between the span areas.
+
+ // MinOptMax newpos ;
+ // if (curSpan != null) {
+ // newpos = curSpan.getPosition(BPD);
+ // newpos.add(curSpan.getDimension(BPD));
+ // }
+ // else newpos = new MinOptMax();
+ curSpan = new Span(numCols);
+ //curSpan.setPosition(BPD, newpos);
+ curBody.getMainReference().addSpan(curSpan);
+ createFlow();
}
// See finishPage...
protected void flush() {
- finishPage();
+ finishPage();
}
}
}
public MinOptMax resolve() {
- return new MinOptMax();
+ return new MinOptMax();
}
}
MinOptMax targetBPD;
public SplitContext(MinOptMax targetBPD) {
- this.targetBPD = targetBPD;
- nextArea = null;
+ this.targetBPD = targetBPD;
+ nextArea = null;
}
}
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.FOText; // For TextInfo: TODO: make independent!
import org.apache.fop.area.Area;
+import org.apache.fop.area.Property;
import org.apache.fop.area.inline.Word;
+import org.apache.fop.util.CharUtilities;
+
+import org.apache.fop.fo.properties.*;
import java.util.ListIterator;
/**
* LayoutManager for text (a sequence of characters) which generates one
- * more inline areas.
+ * or more inline areas.
*/
public class TextLayoutManager extends LeafNodeLayoutManager {
private char[] chars;
private FOText.TextInfo textInfo;
- public TextLayoutManager(FObj fobj, char[] chars,
- FOText.TextInfo textInfo) {
- super(fobj);
- this.chars = chars;
- this.textInfo = textInfo;
+ /* values that prev (below) may take */
+ protected static final int NOTHING = 0;
+ protected static final int WHITESPACE = 1;
+ protected static final int TEXT = 2;
+
+ public TextLayoutManager(FObj fobj, char[] chars,
+ FOText.TextInfo textInfo) {
+ super(fobj);
+ this.chars = chars;
+ this.textInfo = textInfo;
}
/**
* Generate inline areas for words in text.
*/
public void generateAreas() {
- // Handle white-space characteristics. Maybe there is no area to
- // generate....
-
- // Iterate over characters and make text areas.
- // Add each one to parent. Handle word-space.
- Word curWordArea = new Word();
- curWordArea.setWord(new String(chars));
- setCurrentArea(curWordArea);
- flush();
+ // Handle white-space characteristics. Maybe there is no area to
+ // generate....
+
+ // Iterate over characters and make text areas.
+ // Add each one to parent. Handle word-space.
+// Word curWordArea = new Word();
+// curWordArea.setWord(new String(chars));
+//System.out.println("word:" + new String(chars));
+ //parentLM.addChild(curWordArea);
+parseChars();
+
+ //setCurrentArea(curWordArea);
+ //flush();
}
+ protected void parseChars() {
+
+ // With CID fonts, space isn't neccesary currentFontState.width(32)
+ int whitespaceWidth = CharUtilities.getCharWidth(' ', textInfo.fs);
+
+ int wordStart = 0;
+ int wordLength = 0;
+ int wordWidth = 0;
+ int spaceWidth = 0;
+
+ int prev = NOTHING;
+
+ boolean isText = false;
+
+ /* iterate over each character */
+ for (int i = 0; i < chars.length; i++) {
+ int charWidth;
+ /* get the character */
+ char c = chars[i];
+ if (!(CharUtilities.isSpace(c) || (c == '\n') || (c == '\r') || (c == '\t')
+ || (c == '\u2028'))) {
+ charWidth = CharUtilities.getCharWidth(c, textInfo.fs);
+ isText = true;
+ prev = TEXT;
+wordLength++;
+wordWidth += charWidth;
+ // Add support for zero-width spaces
+ if (charWidth <= 0 && c != '\u200B' && c != '\uFEFF')
+ charWidth = whitespaceWidth;
+ } else {
+ if ((c == '\n') || (c == '\r') || (c == '\t'))
+ charWidth = whitespaceWidth;
+ else
+ charWidth = CharUtilities.getCharWidth(c, textInfo.fs);
+
+ isText = false;
+
+ if (prev == WHITESPACE) {
+
+ // if current & previous are WHITESPACE
+
+ if (textInfo.whiteSpaceCollapse == WhiteSpaceCollapse.FALSE) {
+ if (CharUtilities.isSpace(c)) {
+ spaceWidth += CharUtilities.getCharWidth(c, textInfo.fs);
+ } else if (c == '\n' || c == '\u2028') {
+ // force line break
+ if (spaceWidth > 0) {
+ /*InlineSpace is = new InlineSpace(spaceWidth);
+ addChild(is);*/
+ spaceWidth = 0;
+ }
+ } else if (c == '\t') {
+ spaceWidth += 8 * whitespaceWidth;
+ }
+ } else if (c == '\u2028') {
+ // Line separator
+ // Breaks line even if WhiteSpaceCollapse = True
+ if (spaceWidth > 0) {
+ /*InlineSpace is = new InlineSpace(spaceWidth);
+ is.setUnderlined(textState.getUnderlined());
+ is.setOverlined(textState.getOverlined());
+ is.setLineThrough(textState.getLineThrough());
+ addChild(is);*/
+ spaceWidth = 0;
+ }
+ }
+
+ } else if (prev == TEXT) {
+
+ // if current is WHITESPACE and previous TEXT
+ // the current word made it, so
+ // add the space before the current word (if there
+ // was some)
+
+ if (spaceWidth > 0) {
+ /*InlineSpace is = new InlineSpace(spaceWidth);
+ if (prevUlState) {
+ is.setUnderlined(textState.getUnderlined());
+ }
+ if (prevOlState) {
+ is.setOverlined(textState.getOverlined());
+ }
+ if (prevLTState) {
+ is.setLineThrough(textState.getLineThrough());
+ }
+ addChild(is);*/
+ spaceWidth = 0;
+ }
+ // add the current word
+
+ if (wordLength > 0) {
+ // The word might contain nonbreaking
+ // spaces. Split the word and add InlineSpace
+ // as necessary. All spaces inside the word
+ // Have a fixed width.
+ Word curWordArea = new Word();
+curWordArea.setWidth(wordWidth);
+ curWordArea.setWord(new String(chars, wordStart, wordLength + 1));
+Property prop = new Property();
+prop.propType = Property.FONT_STATE;
+prop.data = textInfo.fs;
+curWordArea.addProperty(prop);
+ parentLM.addChild(curWordArea);
+
+ // reset word width
+ wordWidth = 0;
+ }
+
+ // deal with this new whitespace following the
+ // word we just added
+ prev = WHITESPACE;
+
+ spaceWidth = CharUtilities.getCharWidth(c, textInfo.fs);
+
+ if (textInfo.whiteSpaceCollapse == WhiteSpaceCollapse.FALSE) {
+ if (c == '\n' || c == '\u2028') {
+ // force a line break
+ } else if (c == '\t') {
+ spaceWidth = whitespaceWidth;
+ }
+ } else if (c == '\u2028') {
+ }
+ } else {
+
+ // if current is WHITESPACE and no previous
+
+ if (textInfo.whiteSpaceCollapse == WhiteSpaceCollapse.FALSE) {
+ if (CharUtilities.isSpace(c)) {
+ prev = WHITESPACE;
+ spaceWidth = CharUtilities.getCharWidth(c, textInfo.fs);
+ } else if (c == '\n') {
+ // force line break
+ // textdecoration not used because spaceWidth is 0
+ /*InlineSpace is = new InlineSpace(spaceWidth);
+ addChild(is);*/
+ } else if (c == '\t') {
+ prev = WHITESPACE;
+ spaceWidth = 8 * whitespaceWidth;
+ }
+
+ } else {
+ // skip over it
+ wordStart++;
+ }
+ }
+ wordStart = i;
+wordLength = 0;
+ }
+ } // end of iteration over text
+
+ if (wordLength > 0) {
+ // The word might contain nonbreaking
+ // spaces. Split the word and add InlineSpace
+ // as necessary. All spaces inside the word
+ // Have a fixed width.
+if(wordStart + wordLength > chars.length - 1) {
+wordLength = chars.length - 1 - wordStart;
+}
+
+ Word curWordArea = new Word();
+curWordArea.setWidth(wordWidth);
+ curWordArea.setWord(new String(chars, wordStart, wordLength + 1));
+Property prop = new Property();
+prop.propType = Property.FONT_STATE;
+prop.data = textInfo.fs;
+curWordArea.addProperty(prop);
+ parentLM.addChild(curWordArea);
+
+ }
+
+ chars = null;
+ }
/** Try to split the word area by hyphenating the word. */
public boolean splitArea(Area areaToSplit, SplitContext context) {
- context.nextArea = areaToSplit;
- return false;
+ context.nextArea = areaToSplit;
+ return false;
}
-
}
+
protected void renderRegionViewport(RegionViewport port) {
if (port != null) {
Rectangle2D view = port.getViewArea();
- currentBPPosition = (int) view.getY();
- currentIPPosition = (int) view.getX();
+ currentBPPosition = (int) (view.getY() / 1000);
+ currentIPPosition = (int) (view.getX() / 1000);
currentBlockIPPosition = currentIPPosition;
RegionReference region = port.getRegion();
}
protected void renderBlock(Block block) {
- boolean childrenblocks = block.isChildrenBlocks();
List children = block.getChildAreas();
- if (childrenblocks) {
- renderBlocks(children);
+ if (children == null) {
+ // simply move position
} else {
- if (children == null) {
- // simply move position
- } else {
- // a line area is rendered from the top left position
- // of the line, each inline object is offset from there
- for (int count = 0; count < children.size(); count++) {
- LineArea line = (LineArea) children.get(count);
- currentBlockIPPosition = currentIPPosition;
- renderLineArea(line);
- currentBPPosition += line.getHeight();
- }
-
- }
+ renderBlocks(children);
}
}
protected void renderBlocks(List blocks) {
for (int count = 0; count < blocks.size(); count++) {
- Block block = (Block) blocks.get(count);
- renderBlock(block);
+ Object obj = blocks.get(count);
+ if(obj instanceof Block) {
+ renderBlock((Block)obj);
+ } else {
+ // a line area is rendered from the top left position
+ // of the line, each inline object is offset from there
+ LineArea line = (LineArea) obj;
+ currentBlockIPPosition = currentIPPosition;
+ renderLineArea(line);
+ currentBPPosition += line.getHeight();
+ }
}
}
}
*/
package org.apache.fop.render;
+import org.apache.log.Logger;
+
import java.util.HashMap;
/**
*/
public class RendererContext {
String mime;
+ Logger log;
HashMap props = new HashMap();
public RendererContext(String m) {
return mime;
}
+ public void setLogger(Logger logger) {
+ log = logger;
+ }
+
+ public Logger getLogger() {
+ return log;
+ }
+
public void setProperty(String name, Object val) {
props.put(name, val);
}
import org.apache.fop.area.*;
import org.apache.fop.area.inline.*;
+import org.apache.fop.area.inline.Character;
+import org.apache.fop.layout.FontState;
import org.w3c.dom.Document;
import java.io.OutputStream;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
+import java.util.List;
/*
TODO:
*/
PDFPage currentPage;
+ // drawing state
PDFColor currentColor;
+ String currentFontName = "";
+ int currentFontSize = 0;
+ int pageHeight;
/**
* true if a TJ command is left to be written
Rectangle2D bounds = page.getViewArea();
double w = bounds.getWidth();
double h = bounds.getHeight();
+ pageHeight = (int)h;
currentPage = this.pdfDoc.makePage(this.pdfResources,
(int) Math.round(w / 1000), (int) Math.round(h / 1000));
pageReferences.put(page, currentPage.referencePDF());
this.pdfDoc.output(ostream);
}
+ public void renderCharacter(Character ch) {
+
+ super.renderCharacter(ch);
+ }
+
+ public void renderWord(Word word) {
+ StringBuffer pdf = new StringBuffer();
+
+ FontState fs = null;
+
+ List props = word.getPropertyList();
+ for(int count = 0; count < props.size(); count++) {
+ Property pro = (Property)props.get(count);
+ if(pro.propType == Property.FONT_STATE) {
+ fs = (FontState)pro.data;
+ }
+ }
+ String name = fs.getFontName();
+ int size = fs.getFontSize();
+
+ // This assumes that *all* CIDFonts use a /ToUnicode mapping
+ boolean useMultiByte = false;
+ Font f = (Font)fs.getFontInfo().getFonts().get(name);
+ if (f instanceof LazyFont){
+ if(((LazyFont) f).getRealFont() instanceof CIDFont){
+ useMultiByte = true;
+ }
+ } else if (f instanceof CIDFont){
+ useMultiByte = true;
+ }
+ // String startText = useMultiByte ? "<FEFF" : "(";
+ String startText = useMultiByte ? "<" : "(";
+ String endText = useMultiByte ? "> " : ") ";
+
+ updateFont(name, size, pdf);
+ updateColor(true, pdf);
+
+ int rx = currentBlockIPPosition;
+ int bl = pageHeight - currentBPPosition;
+
+ // Set letterSpacing
+ //float ls = fs.getLetterSpacing() / this.currentFontSize;
+ //pdf.append(ls).append(" Tc\n");
+
+ if (!textOpen || bl != prevWordY) {
+ closeText();
+
+ pdf.append("1 0 0 1 " + (rx / 1000f) + " " + (bl / 1000f)
+ + " Tm [" + startText);
+ prevWordY = bl;
+ textOpen = true;
+ } else {
+ // express the space between words in thousandths of an em
+ int space = prevWordX - rx + prevWordWidth;
+ float emDiff = (float)space / (float)currentFontSize * 1000f;
+ // this prevents a problem in Acrobat Reader and other viewers
+ // where large numbers cause text to disappear or default to
+ // a limit
+ if (emDiff < -33000) {
+ closeText();
+
+ pdf.append("1 0 0 1 " + (rx / 1000f) + " " + (bl / 1000f)
+ + " Tm [" + startText);
+ textOpen = true;
+ } else {
+ pdf.append(Float.toString(emDiff));
+ pdf.append(" ");
+ pdf.append(startText);
+ }
+ }
+ prevWordWidth = word.getWidth();
+ prevWordX = rx;
+
+ String s = word.getWord();
+
+ escapeText(s, fs, useMultiByte, pdf);
+ pdf.append(endText);
+
+ currentStream.add(pdf.toString());
+
+ super.renderWord(word);
+ }
+
+ public void escapeText(String s, FontState fs, boolean useMultiByte, StringBuffer pdf) {
+ String startText = useMultiByte ? "<" : "(";
+ String endText = useMultiByte ? "> " : ") ";
+
+ boolean kerningAvailable = false;
+ HashMap kerning = null;
+ kerning = fs.getKerning();
+ if (kerning != null &&!kerning.isEmpty()) {
+ kerningAvailable = true;
+ }
+
+ int l = s.length();
+
+ for (int i = 0; i < l; i++) {
+ char ch = fs.mapChar(s.charAt(i));
+
+ if (!useMultiByte) {
+ if (ch > 127) {
+ pdf.append("\\");
+ pdf.append(Integer.toOctalString((int)ch));
+ } else {
+ switch (ch) {
+ case '(':
+ case ')':
+ case '\\':
+ pdf.append("\\");
+ break;
+ }
+ pdf.append(ch);
+ }
+ } else {
+ pdf.append(getUnicodeString(ch));
+ }
+
+ if (kerningAvailable && (i + 1) < l) {
+ addKerning(pdf, (new Integer((int)ch)),
+ (new Integer((int)fs.mapChar(s.charAt(i + 1)))),
+ kerning, startText, endText);
+ }
+ }
+ }
+
+ /**
+ * Convert a char to a multibyte hex representation
+ */
+ private String getUnicodeString(char c) {
+ StringBuffer buf = new StringBuffer(4);
+
+ byte[] uniBytes = null;
+ try {
+ char[] a = {c};
+ uniBytes = new String(a).getBytes("UnicodeBigUnmarked");
+ } catch (java.io.UnsupportedEncodingException e) {
+ // This should never fail
+ }
+
+ for (int i = 0; i < uniBytes.length; i++) {
+ int b = (uniBytes[i] < 0) ? (int)(256 + uniBytes[i])
+ : (int)uniBytes[i];
+
+ String hexString = Integer.toHexString(b);
+ if (hexString.length() == 1)
+ buf = buf.append("0" + hexString);
+ else
+ buf = buf.append(hexString);
+ }
+ return buf.toString();
+ }
+
+ private void addKerning(StringBuffer buf, Integer ch1, Integer ch2,
+ HashMap kerning, String startText,
+ String endText) {
+ HashMap kernPair = (HashMap)kerning.get(ch1);
+
+ if (kernPair != null) {
+ Integer width = (Integer)kernPair.get(ch2);
+ if (width != null) {
+ buf.append(endText).append(-(width.intValue())).append(' ').append(startText);
+ }
+ }
+ }
+
+ /**
+ * Checks to see if we have some text rendering commands open
+ * still and writes out the TJ command to the stream if we do
+ */
+ private void closeText() {
+ if (textOpen) {
+ currentStream.add("] TJ\n");
+ textOpen = false;
+ prevWordX = 0;
+ prevWordY = 0;
+ }
+ }
+
+ private void updateColor(boolean fill, StringBuffer pdf) {
+ /*PDFColor areaColor = null;
+ if (this.currentFill instanceof PDFColor) {
+ areaColor = (PDFColor)this.currentFill;
+ }
+
+ if (areaColor == null || areaColor.red() != (double)area.getRed()
+ || areaColor.green() != (double)area.getGreen()
+ || areaColor.blue() != (double)area.getBlue()) {
+
+ areaColor = new PDFColor((double)area.getRed(),
+ (double)area.getGreen(),
+ (double)area.getBlue());
+
+ closeText();
+ this.currentFill = areaColor;
+ pdf.append(this.currentFill.getColorSpaceOut(true));
+ }*/
+ }
+
+ private void updateFont(String name, int size, StringBuffer pdf) {
+ if ((!name.equals(this.currentFontName))
+ || (size != this.currentFontSize)) {
+ closeText();
+
+ this.currentFontName = name;
+ this.currentFontSize = size;
+ pdf = pdf.append("/" + name + " " + (size / 1000) + " Tf\n");
+ }
+ }
}
+
File destdir;
File compare = null;
String baseDir = "./";
- Hashtable differ = new Hashtable();
+ HashMap differ = new HashMap();
private Logger log;
/**
* This runs the tests specified in the xml file fname.
* The document is read as a dom and each testcase is covered.
*/
- public Hashtable runTests(String fname, String dest, String compDir) {
+ public HashMap runTests(String fname, String dest, String compDir) {
log.debug("running tests in file:" + fname);
try {
if (compDir != null) {
+++ /dev/null
-/*
- * $Id$
- * Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
- * For details on use and redistribution please refer to the
- * LICENSE file included with these sources.
- */
-
-package org.apache.fop.tools.anttasks;
-
-/**
- * This class is an extension of Ant, a script utility from
- * jakarta.apache.org.
- * It takes a couple of xml files conforming to the xml-site dtd and
- * writes them all into one xml file, deleting any reference to
- * the proprietary protocol sbk. The configFile determines what files
- * are read in what sequence.
- */
-// Ant
-import org.apache.tools.ant.Task;
-import org.apache.tools.ant.BuildException;
-
-
-// SAX
-import org.xml.sax.Parser;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
-import org.xml.sax.Locator;
-import org.xml.sax.AttributeList;
-
-// Java
-import java.io.*;
-import java.util.*;
-import java.net.URL;
-
-public class CompileXMLFiles extends Task
- implements org.xml.sax.EntityResolver, org.xml.sax.DTDHandler,
- org.xml.sax.DocumentHandler, org.xml.sax.ErrorHandler {
- private String configFile, outFile;
- private String[] filenameList;
- private String filenames;
- private ArrayList files = new ArrayList();
-
- // sets name of configuration file, which must
- // be an xml file conforming to the book.dtd used by xml-site
- public void setConfigFile(String configFile) {
- this.configFile = configFile;
- }
-
- public void setOutFile(String outFile) {
- this.outFile = outFile;
- }
-
-
- // main method of this task
- public void execute() throws BuildException {
- boolean errors = false;
-
- if (!(new File(configFile).exists())) {
- errors = true;
- System.err.println("Task CompileXMLFiles - ERROR: config file "
- + configFile + " is missing.");
- }
-
- Parser parser = createParser();
-
- if (parser == null) {
- System.err.println("Task CompileXMLFiles - ERROR: Unable to create SAX parser");
- errors = true;
- }
- parser.setDocumentHandler(this);
- try {
- parser.parse(CompileXMLFiles.fileInputSource(configFile));
- } catch (SAXException e) {
- System.out.println(e);
- } catch (IOException ioe) {
- System.out.println(ioe);
- }
- } // end: execute()
-
-
- /* the following methods belong to the sax parser and implement the Document Handler */
- public InputSource resolveEntity(String publicId,
- String systemId) throws SAXException {
- return null;
- }
-
- public void notationDecl(String name, String publicId, String systemId) {
- // no op
- }
-
- public void unparsedEntityDecl(String name, String publicId,
- String systemId, String notationName) {
- // no op
- }
-
- public void setDocumentLocator(Locator locator) {
- // no op
- }
-
- public void startDocument() throws SAXException {
- // no op
- }
-
- /*
- * After the cnfiguration file has been parsed all files which
- * have been collected in the ArrayList files are concatinated
- * and written to a new (temporary) file
- */
- public void endDocument() throws SAXException {
- String line, filename;
- BufferedReader in;
- try {
- BufferedWriter out =
- new BufferedWriter(new FileWriter("compileXMLFiles-tmp.xml"));
- out.write("<?xml version=\"1.0\"?>\n"
- + "<!DOCTYPE documentation [\n"
- + "<!ENTITY nbsp \" \">\n" + "]>\n<documentation>");
- for(int count = 0; count < files.size(); count++) {
- filename = (String)files.get(count);
- in = new BufferedReader(new FileReader(filename));
- while ((line = in.readLine()) != null) {
- // kill the lines pointing to the sbk protocol and the xml declaration
- if (line.indexOf("<!DOCTYPE ") != -1
- || line.indexOf("<?xml ") != -1) {
- line = "";
- }
- out.write(line + "\n");
- }
- out.flush();
- }
- out.write("\n</documentation>");
- out.close();
- } catch (Exception e) {
- System.out.println(e);
- }
-
- }
-
- public void startElement(String name,
- AttributeList atts) throws SAXException {
- String id, label, source;
- if (name.equals("document") || name.equals("entry")) {
- source = atts.getValue("source");
- files.add(source);
- }
- }
-
- public void endElement(String name) throws SAXException {
- // no op
- }
-
- public void characters(char ch[], int start,
- int length) throws SAXException {
- // no op
- }
-
- public void ignorableWhitespace(char ch[], int start,
- int length) throws SAXException {
- // no op
- }
-
- public void processingInstruction(String target,
- String data) throws SAXException {
- // no op
- }
-
- public void warning(SAXParseException e) throws SAXException {
- // no op
- }
-
- public void error(SAXParseException e) throws SAXException {
- // no op
- }
-
- public void fatalError(SAXParseException e) throws SAXException {
- throw e;
- }
-
- /* ------------------------ */
-
- /**
- * creates a SAX parser, using the value of org.xml.sax.parser
- * defaulting to org.apache.xerces.parsers.SAXParser
- *
- * @return the created SAX parser
- */
- static Parser createParser() {
- String parserClassName = System.getProperty("org.xml.sax.parser");
- if (parserClassName == null) {
- parserClassName = "org.apache.xerces.parsers.SAXParser";
- }
- System.err.println("using SAX parser " + parserClassName);
-
- try {
- return (Parser)Class.forName(parserClassName).newInstance();
- } catch (ClassNotFoundException e) {
- System.err.println("Could not find " + parserClassName);
- } catch (InstantiationException e) {
- System.err.println("Could not instantiate " + parserClassName);
- } catch (IllegalAccessException e) {
- System.err.println("Could not access " + parserClassName);
- } catch (ClassCastException e) {
- System.err.println(parserClassName + " is not a SAX driver");
- }
- return null;
- }
-
- /**
- * create an InputSource from a file name
- *
- * @param filename the name of the file
- * @return the InputSource created
- */
- protected static InputSource fileInputSource(String filename) {
-
- /* this code adapted from James Clark's in XT */
- File file = new File(filename);
- String path = file.getAbsolutePath();
- String fSep = System.getProperty("file.separator");
- if (fSep != null && fSep.length() == 1)
- path = path.replace(fSep.charAt(0), '/');
- if (path.length() > 0 && path.charAt(0) != '/')
- path = '/' + path;
- try {
- return new InputSource(new URL("file", null, path).toString());
- } catch (java.net.MalformedURLException e) {
- throw new Error("unexpected MalformedURLException");
- }
- }
-
-}
-
ClassLoader loader = new URLClassLoader(new URL[] {
new URL("file:build/fop.jar")
});
- Hashtable diff = runConverter(loader, "areatree",
+ HashMap diff = runConverter(loader, "areatree",
"reference/output/");
if (diff != null &&!diff.isEmpty()) {
System.out.println("====================================");
System.out.println("The following files differ:");
boolean broke = false;
- for (Enumeration keys = diff.keys();
- keys.hasMoreElements(); ) {
- Object fname = keys.nextElement();
+ for (Iterator keys = diff.keySet().iterator();
+ keys.hasNext(); ) {
+ Object fname = keys.next();
Boolean pass = (Boolean)diff.get(fname);
System.out.println("file: " + fname
+ " - reference success: " + pass);
* file in the base directory.
* @param loader the class loader to use to run the tests with
*/
- protected Hashtable runConverter(ClassLoader loader, String dest,
+ protected HashMap runConverter(ClassLoader loader, String dest,
String compDir) {
String converter = "org.apache.fop.tools.TestConverter";
- Hashtable diff = null;
+ HashMap diff = null;
try {
Class cla = Class.forName(converter, true, loader);
Object tc = cla.newInstance();
meth = cla.getMethod("runTests", new Class[] {
String.class, String.class, String.class
});
- diff = (Hashtable)meth.invoke(tc, new Object[] {
+ diff = (HashMap)meth.invoke(tc, new Object[] {
testsuite, dest, compDir
});
} catch (Exception e) {
* Die Klasse <code>SecureResourceBundle</code> ist ein Resourceundle, das im Falle eines fehlenden
* Eintrages keinen Absturz verursacht, sondern die Meldung
* <strong>Key <i>key</i> not found</strong> zurückgibt.
- * @see PropertyResourceBundle
*
* @author Stanislav.Gorkhover@jCatalog.com
* @version 1.0 18.03.1999
}
}
-
-
public Enumeration getKeys() {
return lookup.keys();
}
-
-
/**
* Händelt den abgefragten Key, liefert entweder den zugehörigen Wert oder eine Meldung.
* Die <strong>null</strong> wird nie zurückgegeben.
public boolean isSourceFound() {
return isSourceFound;
}
-
}
+