1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /* $Id: FootnoteLayoutManager.java 1362226 2012-07-16 19:29:06Z vhennebert $ */ 19 20 package org.apache.fop.layoutmgr.inline; 21 22 import java.util.LinkedList; 23 import java.util.List; 24 import java.util.ListIterator; 25 26 import org.apache.commons.logging.Log; 27 import org.apache.commons.logging.LogFactory; 28 29 import org.apache.fop.fo.flow.Footnote; 30 import org.apache.fop.layoutmgr.FootnoteBodyLayoutManager; 31 import org.apache.fop.layoutmgr.InlineKnuthSequence; 32 import org.apache.fop.layoutmgr.KnuthElement; 33 import org.apache.fop.layoutmgr.KnuthSequence; 34 import org.apache.fop.layoutmgr.LayoutContext; 35 import org.apache.fop.layoutmgr.LayoutManager; 36 import org.apache.fop.layoutmgr.ListElement; 37 import org.apache.fop.layoutmgr.NonLeafPosition; 38 import org.apache.fop.layoutmgr.Position; 39 import org.apache.fop.layoutmgr.PositionIterator; 40 41 /** 42 * Layout manager for fo:footnote. 43 */ 44 public class FootnoteLayoutManager extends InlineStackingLayoutManager { 45 46 /** 47 * logging instance 48 */ 49 private static Log log = LogFactory.getLog(FootnoteLayoutManager.class); 50 51 private Footnote footnote; 52 private InlineStackingLayoutManager citationLM; 53 private FootnoteBodyLayoutManager bodyLM; 54 /** Represents the footnote citation **/ 55 private KnuthElement forcedAnchor; 56 57 /** 58 * Create a new footnote layout manager. 59 * @param node footnote to create the layout manager for 60 */ FootnoteLayoutManager(Footnote node)61 public FootnoteLayoutManager(Footnote node) { 62 super(node); 63 footnote = node; 64 } 65 66 /** {@inheritDoc} */ 67 @Override initialize()68 public void initialize() { 69 // create an InlineStackingLM handling the fo:inline child of fo:footnote 70 citationLM = new InlineLayoutManager(footnote.getFootnoteCitation()); 71 72 // create a FootnoteBodyLM handling the fo:footnote-body child of fo:footnote 73 bodyLM = new FootnoteBodyLayoutManager(footnote.getFootnoteBody()); 74 } 75 76 /** {@inheritDoc} */ 77 @Override getNextKnuthElements(LayoutContext context, int alignment)78 public List getNextKnuthElements(LayoutContext context, 79 int alignment) { 80 // for the moment, this LM is set as the citationLM's parent 81 // later on, when this LM will have nothing more to do, the citationLM's parent 82 // will be set to the fotnoteLM's parent 83 citationLM.setParent(this); 84 citationLM.initialize(); 85 bodyLM.setParent(this); 86 bodyLM.initialize(); 87 88 // get Knuth elements representing the footnote citation 89 List returnedList = new LinkedList(); 90 while (!citationLM.isFinished()) { 91 List partialList = citationLM.getNextKnuthElements(context, alignment); 92 if (partialList != null) { 93 returnedList.addAll(partialList); 94 } 95 } 96 if (returnedList.size() == 0) { 97 //Inline part of the footnote is empty. Need to send back an auxiliary 98 //zero-width, zero-height inline box so the footnote gets painted. 99 KnuthSequence seq = new InlineKnuthSequence(); 100 //Need to use an aux. box, otherwise, the line height can't be forced to zero height. 101 forcedAnchor = new KnuthInlineBox(0, null, null, true); 102 seq.add(forcedAnchor); 103 returnedList.add(seq); 104 } 105 setFinished(true); 106 107 addAnchor(returnedList); 108 109 // "wrap" the Position stored in each list inside returnedList 110 ListIterator listIterator = returnedList.listIterator(); 111 ListIterator elementIterator = null; 112 KnuthSequence list = null; 113 ListElement element = null; 114 while (listIterator.hasNext()) { 115 list = (KnuthSequence) listIterator.next(); 116 elementIterator = list.listIterator(); 117 while (elementIterator.hasNext()) { 118 element = (KnuthElement) elementIterator.next(); 119 element.setPosition(notifyPos(new NonLeafPosition(this, element.getPosition()))); 120 } 121 } 122 123 return returnedList; 124 } 125 126 /** {@inheritDoc} */ 127 @Override getChangedKnuthElements(List oldList, int alignment, int depth)128 public List getChangedKnuthElements(List oldList, int alignment, int depth) { 129 List returnedList = super.getChangedKnuthElements(oldList, alignment, depth); 130 addAnchor(returnedList); 131 return returnedList; 132 } 133 134 135 /** {@inheritDoc} */ 136 @Override addAreas(PositionIterator posIter, LayoutContext context)137 public void addAreas(PositionIterator posIter, LayoutContext context) { 138 // "Unwrap" the NonLeafPositions stored in posIter and put 139 // them in a new list, that will be given to the citationLM 140 LinkedList<Position> positionList = new LinkedList<Position>(); 141 Position pos; 142 while (posIter.hasNext()) { 143 pos = posIter.next(); 144 if (pos != null && pos.getPosition() != null) { 145 positionList.add(pos.getPosition()); 146 } 147 } 148 149 // FootnoteLM does not create any area, 150 // so the citationLM child will add directly to the FootnoteLM parent area 151 citationLM.setParent(getParent()); 152 153 // make the citationLM add its areas 154 LayoutContext childContext = LayoutContext.copyOf(context); 155 PositionIterator childPosIter = new PositionIterator(positionList.listIterator()); 156 LayoutManager childLM; 157 while ((childLM = childPosIter.getNextChildLM()) != null) { 158 childLM.addAreas(childPosIter, childContext); 159 childContext.setLeadingSpace(childContext.getTrailingSpace()); 160 childContext.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true); 161 } 162 } 163 164 /** 165 * Find the last box in the sequence, and add a reference to the FootnoteBodyLM 166 * @param citationList the list of elements representing the footnote citation 167 */ addAnchor(List citationList)168 private void addAnchor(List citationList) { 169 KnuthInlineBox lastBox = null; 170 // the list of elements is searched backwards, until we find a box 171 ListIterator citationIterator = citationList.listIterator(citationList.size()); 172 while (citationIterator.hasPrevious() && lastBox == null) { 173 Object obj = citationIterator.previous(); 174 if (obj instanceof KnuthElement) { 175 // obj is an element 176 KnuthElement element = (KnuthElement)obj; 177 if (element instanceof KnuthInlineBox) { 178 lastBox = (KnuthInlineBox) element; 179 } 180 } else { 181 // obj is a sequence of elements 182 KnuthSequence seq = (KnuthSequence)obj; 183 ListIterator nestedIterator = seq.listIterator(seq.size()); 184 while (nestedIterator.hasPrevious() && lastBox == null) { 185 KnuthElement element = (KnuthElement)nestedIterator.previous(); 186 if (element instanceof KnuthInlineBox && !element.isAuxiliary() 187 || element == forcedAnchor) { 188 lastBox = (KnuthInlineBox) element; 189 } 190 } 191 } 192 } 193 if (lastBox != null) { 194 lastBox.setFootnoteBodyLM(bodyLM); 195 } else { 196 //throw new IllegalStateException("No anchor box was found for a footnote."); 197 } 198 } 199 } 200