aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java
blob: 44e0cc476ff546a0a58a7848ecc8675115b50dc9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
/*
 * 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.layoutmgr;

import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
import org.apache.fop.area.RegionReference;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.pagination.PageSequence;
import org.apache.fop.fo.pagination.SideRegion;
import org.apache.fop.fo.pagination.StaticContent;
import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener;
import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager;
import org.apache.fop.layoutmgr.inline.TextLayoutManager;
import org.apache.fop.traits.MinOptMax;
import org.apache.fop.util.ListUtil;

/**
 * LayoutManager for an fo:flow object.
 * Its parent LM is the PageSequenceLayoutManager.
 * This LM is responsible for getting columns of the appropriate size
 * and filling them with block-level areas generated by its children.
 */
public class StaticContentLayoutManager extends BlockStackingLayoutManager {

    /**
     * logging instance
     */
    private static Log log = LogFactory.getLog(StaticContentLayoutManager.class);

    private RegionReference targetRegion;
    private Block targetBlock;
    private SideRegion regionFO;

    private int contentAreaIPD = 0;
    private int contentAreaBPD = -1;

    /**
     * Creates a new StaticContentLayoutManager.
     * @param pslm PageSequenceLayoutManager this layout manager belongs to
     * @param node static-content FO
     * @param reg side region to layout into
     */
    public StaticContentLayoutManager(PageSequenceLayoutManager pslm, 
            StaticContent node, SideRegion reg) {
        super(node);
        setParent(pslm);
        regionFO = reg;
        targetRegion = getCurrentPV().getRegionReference(regionFO.getNameId());
    }

    /**
     * Creates a new StaticContentLayoutManager.
     * @param pslm PageSequenceLayoutManager this layout manager belongs to
     * @param node static-content FO
     * @param block the block to layout into
     */
    public StaticContentLayoutManager(PageSequenceLayoutManager pslm, 
            StaticContent node, Block block) {
        super(node);
        setParent(pslm);
        targetBlock = block; 
    }

    /** {@inheritDoc} */
    public List getNextKnuthElements(LayoutContext context, int alignment) {
        if (true) {
            throw new UnsupportedOperationException(
                "Shouldn't this method be emptied because it's never called at all?");
        }
        //TODO Empty this method?!?
        // set layout dimensions
        setContentAreaIPD(context.getRefIPD());
        setContentAreaBPD(context.getStackLimitBP().opt);

        //TODO Copied from elsewhere. May be worthwhile to factor out the common parts. 
        // currently active LM
        BlockLevelLayoutManager curLM;
        BlockLevelLayoutManager prevLM = null;
        MinOptMax stackSize = new MinOptMax();
        List returnedList;
        List returnList = new LinkedList();

        while ((curLM = ((BlockLevelLayoutManager) getChildLM())) != null) {
            if (curLM instanceof InlineLevelLayoutManager) {
                log.error("inline area not allowed under flow - ignoring");
                curLM.setFinished(true);
                continue;
            }

            // Set up a LayoutContext
            MinOptMax bpd = context.getStackLimitBP();

            LayoutContext childLC = new LayoutContext(0);
            childLC.setStackLimitBP(MinOptMax.subtract(bpd, stackSize));
            childLC.setRefIPD(context.getRefIPD());

            // get elements from curLM
            returnedList = curLM.getNextKnuthElements(childLC, alignment);
            //log.debug("FLM.getNextKnuthElements> returnedList.size() = " 
            //    + returnedList.size());

            // "wrap" the Position inside each element
            List tempList = returnedList;
            KnuthElement tempElement;
            returnedList = new LinkedList();
            ListIterator listIter = tempList.listIterator();
            while (listIter.hasNext()) {
                tempElement = (KnuthElement)listIter.next();
                tempElement.setPosition(new NonLeafPosition(this, tempElement.getPosition()));
                returnedList.add(tempElement);
            }

            if (returnedList.size() == 1
                && ((KnuthElement)returnedList.get(0)).isPenalty()
                && ((KnuthPenalty)returnedList.get(0)).getP() == -KnuthElement.INFINITE) {
                // a descendant of this flow has break-before
                returnList.addAll(returnedList);
                return returnList;
            } else {
                if (!returnList.isEmpty()) {
                    // there is a block before this one
                    if (prevLM.mustKeepWithNext()
                        || curLM.mustKeepWithPrevious()) {
                        // add an infinite penalty to forbid a break between blocks
                        returnList.add(new KnuthPenalty(0, 
                                KnuthElement.INFINITE, false, 
                                new Position(this), false));
                    } else if (!((KnuthElement) ListUtil.getLast(returnList))
                            .isGlue()) {
                        // add a null penalty to allow a break between blocks
                        returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
                    }
                }
/*LF*/          if (!returnedList.isEmpty()) { // controllare!
                    returnList.addAll(returnedList);
                    final KnuthElement last = (KnuthElement) ListUtil
                            .getLast(returnedList);
                    if (last.isPenalty()
                            && ((KnuthPenalty) last).getP() == -KnuthElement.INFINITE) {
                        // a descendant of this flow has break-after
/*LF*/                  //log.debug("FLM - break after!!");
                        return returnList;
                    }
/*LF*/          }
            }
            prevLM = curLM;
        }

        setFinished(true);

        if (returnList.isEmpty()) {
            return null;
        } else {
            return returnList;
        }
    }
    
    /**
     * {@inheritDoc} 
     */
    public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
        AreaAdditionUtil.addAreas(this, parentIter, layoutContext);

        flush();
        targetRegion = null;
    }


    /**
     * Add child area to a the correct container, depending on its
     * area class. A Flow can fill at most one area container of any class
     * at any one time. The actual work is done by BlockStackingLM.
     * {@inheritDoc}
     */
    public void addChildArea(Area childArea) {
        if (getStaticContentFO().getFlowName().equals("xsl-footnote-separator")) {
            targetBlock.addBlock((Block)childArea);
        } else {
            targetRegion.addBlock((Block)childArea);
        }
    }

    /**
     * {@inheritDoc}
     */
    public Area getParentArea(Area childArea) {
        if (getStaticContentFO().getFlowName().equals("xsl-footnote-separator")) {
            return targetBlock;
        } else {
            return targetRegion;
        }
    }

    /**
     * Does the layout for a side region. Called by PageSequenceLayoutManager.
     */
    public void doLayout() {
        int targetIPD = 0;
        int targetBPD = 0;
        int targetAlign = EN_AUTO;
        boolean autoHeight = false;
        StaticContentBreaker breaker;

        if (getStaticContentFO().getFlowName().equals("xsl-footnote-separator")) {
            targetIPD = targetBlock.getIPD();
            targetBPD = targetBlock.getBPD();
            if (targetBPD == 0) {
                autoHeight = true;
            }
            targetAlign = EN_BEFORE;
        } else {
            targetIPD = targetRegion.getIPD();
            targetBPD = targetRegion.getBPD();
            targetAlign = regionFO.getDisplayAlign();
        }
        setContentAreaIPD(targetIPD);
        setContentAreaBPD(targetBPD);
        breaker = new StaticContentBreaker(this, targetIPD, targetAlign);
        breaker.doLayout(targetBPD, autoHeight);
        if (breaker.isOverflow()) {
            if (!autoHeight) {
                String page = getPSLM().getCurrentPage().getPageViewport().getPageNumberString();
                
                BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
                        getStaticContentFO().getUserAgent().getEventBroadcaster());
                boolean canRecover = (regionFO.getOverflow() != EN_ERROR_IF_OVERFLOW); 
                boolean needClip = (regionFO.getOverflow() == Constants.EN_HIDDEN
                        || regionFO.getOverflow() == Constants.EN_ERROR_IF_OVERFLOW);
                eventProducer.regionOverflow(this, regionFO.getName(),
                        page,
                        breaker.getOverflowAmount(), needClip, canRecover,
                        getStaticContentFO().getLocator());
            }
        }
    }
    
    /**
     * Convenience method that returns the Static Content node.
     * @return the static content node
     */
    protected StaticContent getStaticContentFO() {
        return (StaticContent) fobj;
    }

    private class StaticContentBreaker extends AbstractBreaker {       
        private StaticContentLayoutManager lm;
        private int displayAlign;
        private int ipd;
        private int overflow = 0;
        
        public StaticContentBreaker(StaticContentLayoutManager lm, int ipd, 
                int displayAlign) {
            this.lm = lm;
            this.ipd = ipd;
            this.displayAlign = displayAlign;
        }

        /** {@inheritDoc} */
        protected void observeElementList(List elementList) {
            String elementListID = getStaticContentFO().getFlowName();
            String pageSequenceID = ((PageSequence)lm.getParent().getFObj()).getId();
            if (pageSequenceID != null && pageSequenceID.length() > 0) {
                elementListID += "-" + pageSequenceID;
            }
            ElementListObserver.observe(elementList, "static-content", elementListID);
        }
        
        /** {@inheritDoc} */
        protected boolean isPartOverflowRecoveryActivated() {
            //For side regions, this must be disabled because of wanted overflow.
            return false;
        }

        public boolean isOverflow() {
            return (this.overflow != 0);
        }
        
        public int getOverflowAmount() {
            return this.overflow;
        }
        
        /** {@inheritDoc} */
        protected PageBreakingLayoutListener createLayoutListener() {
            return new PageBreakingLayoutListener() {

                public void notifyOverflow(int part, int amount, FObj obj) {
                    if (StaticContentBreaker.this.overflow == 0) {
                        StaticContentBreaker.this.overflow = amount;
                    }
                }
                
            };
        }

        protected LayoutManager getTopLevelLM() {
            return lm;
        }

        protected LayoutContext createLayoutContext() {
            LayoutContext lc = super.createLayoutContext();
            lc.setRefIPD(ipd);
            return lc;
        }
        
        protected List getNextKnuthElements(LayoutContext context, int alignment) {
            LayoutManager curLM; // currently active LM
            List returnList = new LinkedList();

            while ((curLM = getChildLM()) != null) {
                LayoutContext childLC = new LayoutContext(0);
                childLC.setStackLimitBP(context.getStackLimitBP());
                childLC.setRefIPD(context.getRefIPD());
                childLC.setWritingMode(context.getWritingMode());

                List returnedList = null;
                //The following is a HACK! Ignore leading and trailing white space 
                boolean ignore = curLM instanceof TextLayoutManager;
                if (!curLM.isFinished()) {
                    returnedList = curLM.getNextKnuthElements(childLC, alignment);
                }
                if (returnedList != null && !ignore) {
                    lm.wrapPositionElements(returnedList, returnList);
                }
            }
            SpaceResolver.resolveElementList(returnList);
            setFinished(true);
            return returnList;
        }

        protected int getCurrentDisplayAlign() {
            return displayAlign;
        }
        
        protected boolean hasMoreContent() {
            return !lm.isFinished();
        }
        
        protected void addAreas(PositionIterator posIter, LayoutContext context) {
            AreaAdditionUtil.addAreas(lm, posIter, context);    
        }
        
        protected void doPhase3(PageBreakingAlgorithm alg, int partCount, 
                BlockSequence originalList, BlockSequence effectiveList) {
            if (partCount > 1) {
                PageBreakPosition pos = (PageBreakPosition)alg.getPageBreaks().getFirst();
                int firstPartLength = ElementListUtils.calcContentLength(effectiveList,
                        effectiveList.ignoreAtStart, pos.getLeafPos());
                overflow += alg.totalWidth - firstPartLength;        
            }         
            //Rendering all parts (not just the first) at once for the case where the parts that 
            //overflow should be visible.
            alg.removeAllPageBreaks();
            //Directly add areas after finding the breaks
            this.addAreas(alg, 1, originalList, effectiveList);
        }
        
        protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) {
            //nop for static content
        }
        
        protected LayoutManager getCurrentChildLM() {
            return null; //TODO NYI
        }
    } 
    
    /**
     * Returns the IPD of the content area
     * @return the IPD of the content area
     */
    public int getContentAreaIPD() {
        return contentAreaIPD;
    }
   
    /** {@inheritDoc} */
    protected void setContentAreaIPD(int contentAreaIPD) {
        this.contentAreaIPD = contentAreaIPD;
    }
    
    /**
     * Returns the BPD of the content area
     * @return the BPD of the content area
     */
    public int getContentAreaBPD() {
        return contentAreaBPD;
    }
    
    private void setContentAreaBPD(int contentAreaBPD) {
        this.contentAreaBPD = contentAreaBPD;
    }
    
    /** {@inheritDoc} */
    public int getKeepTogetherStrength() {
        return KEEP_AUTO;
    }
    
    /** {@inheritDoc} */
    public int getKeepWithNextStrength() {
        return KEEP_AUTO;
    }

    /** {@inheritDoc} */
    public int getKeepWithPreviousStrength() {
        return KEEP_AUTO;
    }
    
}