@@ -135,6 +135,8 @@ public class Area extends AreaTreeObject implements Serializable { | |||
*/ | |||
private List<ChangeBar> changeBarList; | |||
private boolean fromFootnote; | |||
/** | |||
* Returns the active change bar list. | |||
* | |||
@@ -540,4 +542,12 @@ public class Area extends AreaTreeObject implements Serializable { | |||
ipd = effectiveIPD; | |||
} | |||
} | |||
public boolean isFromFootnote() { | |||
return fromFootnote; | |||
} | |||
public void setFromFootnote(boolean fromFootnote) { | |||
this.fromFootnote = fromFootnote; | |||
} | |||
} |
@@ -71,8 +71,16 @@ public class InlineParent extends InlineArea { | |||
} | |||
updateLevel(childArea.getBidiLevel()); | |||
int childOffset = childArea.getVirtualOffset(); | |||
minChildOffset = Math.min(minChildOffset, childOffset); | |||
maxAfterEdge = Math.max(maxAfterEdge, childOffset + childArea.getVirtualBPD()); | |||
// do not offset if the childArea comes from a footnote | |||
// or if the parent itself comes from a footnote | |||
if (!(childArea.isFromFootnote() || isFromFootnote())) { | |||
minChildOffset = Math.min(minChildOffset, childOffset); | |||
maxAfterEdge = Math.max(maxAfterEdge, childOffset + childArea.getVirtualBPD()); | |||
} else { | |||
minChildOffset = Math.min(minChildOffset, childArea.getBlockProgressionOffset()); | |||
maxAfterEdge = minChildOffset + childArea.getVirtualBPD(); | |||
} | |||
} | |||
@Override | |||
@@ -148,5 +156,14 @@ public class InlineParent extends InlineArea { | |||
} | |||
} | |||
@Override | |||
public void setFromFootnote(boolean fromFootnote) { | |||
super.setFromFootnote(fromFootnote); | |||
// set all the children to avoid offsetting any of them | |||
// otherwise we would offset the parent by offsetting the child | |||
for (InlineArea area : inlines) { | |||
area.setFromFootnote(fromFootnote); | |||
} | |||
} | |||
} |
@@ -51,6 +51,8 @@ public abstract class AbstractBaseLayoutManager | |||
*/ | |||
private static final Log LOG = LogFactory.getLog(AbstractBaseLayoutManager.class); | |||
private boolean fromFootnote; | |||
/** | |||
* Abstract base layout manager. | |||
*/ | |||
@@ -294,4 +296,12 @@ public abstract class AbstractBaseLayoutManager | |||
public void recreateChildrenLMs() { | |||
} | |||
public boolean isFromFootnote() { | |||
return fromFootnote || (getParent() != null && getParent().isFromFootnote()); | |||
} | |||
public void setFromFootnote(boolean fromFootnote) { | |||
this.fromFootnote = fromFootnote; | |||
} | |||
} |
@@ -85,6 +85,7 @@ public class FootnoteBodyLayoutManager extends BlockStackingLayoutManager { | |||
lc.setFlags(LayoutContext.LAST_AREA, | |||
(layoutContext.isLastArea() && childLM == lastLM)); | |||
// Add the line areas to Area | |||
childLM.setFromFootnote(true); | |||
childLM.addAreas(childPosIter, lc); | |||
} | |||
} | |||
@@ -93,6 +94,7 @@ public class FootnoteBodyLayoutManager extends BlockStackingLayoutManager { | |||
@Override | |||
public void addChildArea(Area childArea) { | |||
childArea.setAreaClass(Area.CLASS_FOOTNOTE); | |||
childArea.setFromFootnote(true); | |||
parentLayoutManager.addChildArea(childArea); | |||
} | |||
@@ -271,4 +271,8 @@ public interface LayoutManager extends PercentBaseContext { | |||
*/ | |||
List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack, | |||
Position positionAtIPDChange, LayoutManager restartAtLM); | |||
boolean isFromFootnote(); | |||
void setFromFootnote(boolean fromFootnote); | |||
} |
@@ -155,6 +155,7 @@ public class FootnoteLayoutManager extends InlineStackingLayoutManager { | |||
PositionIterator childPosIter = new PositionIterator(positionList.listIterator()); | |||
LayoutManager childLM; | |||
while ((childLM = childPosIter.getNextChildLM()) != null) { | |||
childLM.setFromFootnote(true); | |||
childLM.addAreas(childPosIter, childContext); | |||
childContext.setLeadingSpace(childContext.getTrailingSpace()); | |||
childContext.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true); |
@@ -540,6 +540,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager { | |||
// Not sure if lastPos can legally be null or if that masks a different problem. | |||
// But it seems to fix bug 38053. | |||
setTraits(areaCreated, lastPos == null || !isLast(lastPos)); | |||
getCurrentArea().setFromFootnote(isFromFootnote()); | |||
parentLayoutManager.addChildArea(getCurrentArea()); | |||
registerMarkers( |
@@ -187,6 +187,8 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager | |||
if (level >= 0) { | |||
ls.setBidiLevel(level); | |||
} | |||
ls.setFromFootnote(isFromFootnote()); | |||
parentArea.addChildArea(ls); | |||
} | |||
} |
@@ -333,6 +333,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { | |||
textArea.setTextLetterSpaceAdjust(letterSpaceDim); | |||
textArea.setTextWordSpaceAdjust(wordSpaceDim - spaceCharIPD | |||
- 2 * textArea.getTextLetterSpaceAdjust()); | |||
textArea.setFromFootnote(isFromFootnote()); | |||
if (context.getIPDAdjust() != 0) { | |||
// add information about space width | |||
textArea.setSpaceDifference(wordSpaceIPD.getOpt() - spaceCharIPD |
@@ -0,0 +1,116 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.area.inline; | |||
import org.junit.Test; | |||
import static org.junit.Assert.assertEquals; | |||
public class InlineParentTestCase { | |||
private static final int BLOCK_PROG_OFFSET = -12; | |||
private static final int BPD = 25; | |||
@Test | |||
public void testAddChildAreaMixedFromFootnote() { | |||
InlineParent parent = new InlineParent(); | |||
InlineParent firstChild = createChildInlineParent(BLOCK_PROG_OFFSET, BPD, false); | |||
InlineParent secondChild = createChildInlineParent(3 * BLOCK_PROG_OFFSET, 3 * BPD, false); | |||
InlineParent thirdChild = createChildInlineParent(2 * BLOCK_PROG_OFFSET, 2 * BPD, true); | |||
InlineParent forthChild = createChildInlineParent(0, 0, true); | |||
assertEquals("Default Values must be zero", 0, parent.minChildOffset); | |||
assertEquals("Default Values must be zero", 0, parent.getVirtualBPD()); | |||
assertAddChildArea(parent, firstChild, -12, 0); | |||
assertAddChildArea(parent, secondChild, -36, 0); | |||
assertAddChildArea(parent, thirdChild, -36, -36); | |||
assertAddChildArea(parent, forthChild, -36, -36); | |||
} | |||
@Test | |||
public void testAddChildAreaNotFromFootnote() { | |||
InlineParent parent = new InlineParent(); | |||
InlineParent firstChild = createChildInlineParent(BLOCK_PROG_OFFSET, BPD, false); | |||
InlineParent secondChild = createChildInlineParent(3 * BLOCK_PROG_OFFSET, 3 * BPD, false); | |||
InlineParent thirdChild = createChildInlineParent(2 * BLOCK_PROG_OFFSET, 2 * BPD, false); | |||
InlineParent forthChild = createChildInlineParent(0, 0, false); | |||
assertEquals("Default Values must be zero", 0, parent.minChildOffset); | |||
assertEquals("Default Values must be zero", 0, parent.getVirtualBPD()); | |||
assertAddChildArea(parent, firstChild, -12, 0); | |||
assertAddChildArea(parent, secondChild, -36, 0); | |||
assertAddChildArea(parent, thirdChild, -36, 0); | |||
assertAddChildArea(parent, forthChild, -36, 0); | |||
} | |||
@Test | |||
public void testAddChildAreaFromFootnote() { | |||
InlineParent parent = new InlineParent(); | |||
InlineParent firstChild = createChildInlineParent(BLOCK_PROG_OFFSET, BPD, true); | |||
InlineParent secondChild = createChildInlineParent(3 * BLOCK_PROG_OFFSET, 3 * BPD, true); | |||
InlineParent thirdChild = createChildInlineParent(2 * BLOCK_PROG_OFFSET, 2 * BPD, true); | |||
InlineParent forthChild = createChildInlineParent(0, 0, true); | |||
assertEquals("Default Values must be zero", 0, parent.minChildOffset); | |||
assertEquals("Default Values must be zero", 0, parent.getVirtualBPD()); | |||
assertAddChildArea(parent, firstChild, -12, -12); | |||
assertAddChildArea(parent, secondChild, -36, -36); | |||
assertAddChildArea(parent, thirdChild, -36, -36); | |||
assertAddChildArea(parent, forthChild, -36, -36); | |||
} | |||
private void assertAddChildArea(InlineParent parent, InlineParent child, | |||
int minChildOffset, int maxAfterEdge) { | |||
parent.addChildArea(child); | |||
// the virtualBPD is the subtraction of the maxAfterEdge with the minChildOffset | |||
// by adding the minChildOffset to the virtualBPD we get the maxAfterEdge alone | |||
int parentMaxAfterEdge = parent.getVirtualBPD() + parent.minChildOffset; | |||
if (!child.isFromFootnote()) { | |||
assertEquals("Must be set to the min of the current minChildOffset and the " | |||
+ "sum of the child's virtualOffset with the current value of the minChildOffset", | |||
minChildOffset, parent.minChildOffset); | |||
assertEquals("Must be set to the max of the current maxAfterEdge and the " | |||
+ "result of the sum of the child's virtualOffset with child's the virtualBPD", | |||
maxAfterEdge, parentMaxAfterEdge); | |||
} else { | |||
assertEquals("Must be set to the min of the current minChildOffset and the " | |||
+ "sum of the child's blockProgressionOffset with the current value " | |||
+ "of the minChildOffset", | |||
minChildOffset, parent.minChildOffset); | |||
assertEquals("Must be the result of the sum of the maxAfterEdge " | |||
+ "with the child's virtualBPD", maxAfterEdge, parentMaxAfterEdge); | |||
} | |||
} | |||
private InlineParent createChildInlineParent(int bpo, int bpd, boolean footnote) { | |||
InlineParent child = new InlineParent(); | |||
child.setBlockProgressionOffset(bpo); | |||
child.setBPD(bpd); | |||
child.setFromFootnote(footnote); | |||
return child; | |||
} | |||
} |
@@ -0,0 +1,90 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!-- | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
this work for additional information regarding copyright ownership. | |||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||
(the "License"); you may not use this file except in compliance with | |||
the License. You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
--> | |||
<!-- $Id$ --> | |||
<testcase> | |||
<info> | |||
<p> | |||
This test checks footnotes. | |||
</p> | |||
</info> | |||
<fo> | |||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg"> | |||
<fo:layout-master-set> | |||
<fo:simple-page-master master-name="normal" page-width="5in" page-height="3in"> | |||
<fo:region-body/> | |||
</fo:simple-page-master> | |||
</fo:layout-master-set> | |||
<fo:page-sequence master-reference="normal" white-space-collapse="true"> | |||
<fo:flow flow-name="xsl-region-body"> | |||
<fo:block> | |||
<!-- Mid-Link Footnote --> | |||
<fo:block> | |||
<fo:basic-link external-destination="https://www.google.com/"> | |||
<fo:inline>This is a link with a footnote</fo:inline> | |||
<fo:footnote> | |||
<fo:inline> | |||
<fo:basic-link id="fn-ref-2" internal-destination="fn-2" color="blue"> | |||
<fo:inline baseline-shift="super" font-size="80%">1</fo:inline> | |||
</fo:basic-link> | |||
</fo:inline> | |||
<fo:footnote-body> | |||
<fo:block color="black" text-decoration="none"> | |||
<fo:table table-layout="fixed" width="100%"> | |||
<fo:table-column column-width="proportional-column-width(5)"/> | |||
<fo:table-column column-width="proportional-column-width(95)"/> | |||
<fo:table-body> | |||
<fo:table-row> | |||
<fo:table-cell> | |||
<fo:block> | |||
<fo:basic-link id="fn-2" internal-destination="fn-ref-2" color="blue">1</fo:basic-link> | |||
</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell> | |||
<fo:block>Note text</fo:block> | |||
</fo:table-cell> | |||
</fo:table-row> | |||
</fo:table-body> | |||
</fo:table> | |||
</fo:block> | |||
</fo:footnote-body> | |||
</fo:footnote> | |||
<fo:inline> inside</fo:inline> | |||
</fo:basic-link> | |||
</fo:block> | |||
</fo:block> | |||
</fo:flow> | |||
</fo:page-sequence> | |||
</fo:root> | |||
</fo> | |||
<checks> | |||
<!-- block with footnote --> | |||
<eval expected="This is a link with a footnote 1 inside" xpath="//inlineparent[1]"/> | |||
<eval expected=" 1" xpath="//inlineparent[2]"/> | |||
<!-- the footnote --> | |||
<eval expected="4552" xpath="//inlineparent[1]/@offset"/> | |||
<eval expected="11100" xpath="//inlineparent[1]/@bpd"/> | |||
<eval expected="11100" xpath="//inlineparent[1]/@bpda"/> | |||
<eval expected="0" xpath="//inlineparent[2]/@offset"/> | |||
<eval expected="11100" xpath="//inlineparent[2]/@bpd"/> | |||
<eval expected="11100" xpath="//inlineparent[2]/@bpda"/> | |||
<eval expected="15652" xpath="//lineArea/@bpd"/> | |||
<eval expected="18952" xpath="//lineArea/@bpda"/> | |||
</checks> | |||
</testcase> |