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