Line 0
Link Here
|
|
|
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$ */ |
19 |
|
20 |
package org.apache.fop.layoutmgr.inline; |
21 |
|
22 |
// Java |
23 |
import java.util.LinkedList; |
24 |
import java.util.List; |
25 |
import java.util.ListIterator; |
26 |
|
27 |
import org.apache.fop.area.Area; |
28 |
import org.apache.fop.area.Block; |
29 |
import org.apache.fop.area.Trait; |
30 |
import org.apache.fop.area.inline.InlineArea; |
31 |
import org.apache.fop.area.inline.InlineBlockParent; |
32 |
import org.apache.fop.area.inline.Viewport; |
33 |
import org.apache.fop.datatypes.FODimension; |
34 |
import org.apache.fop.datatypes.Length; |
35 |
import org.apache.fop.fo.flow.InlineContainer; |
36 |
import org.apache.fop.fo.properties.CommonBorderPaddingBackground; |
37 |
import org.apache.fop.fo.properties.SpaceProperty; |
38 |
import org.apache.fop.layoutmgr.AbstractContainerBreaker; |
39 |
import org.apache.fop.layoutmgr.AreaAdditionUtil; |
40 |
import org.apache.fop.layoutmgr.BlockStackingLayoutManager; |
41 |
import org.apache.fop.layoutmgr.InlineKnuthSequence; |
42 |
import org.apache.fop.layoutmgr.KnuthElement; |
43 |
import org.apache.fop.layoutmgr.KnuthSequence; |
44 |
import org.apache.fop.layoutmgr.LayoutContext; |
45 |
import org.apache.fop.layoutmgr.LayoutManager; |
46 |
import org.apache.fop.layoutmgr.ListElement; |
47 |
import org.apache.fop.layoutmgr.NonLeafPosition; |
48 |
import org.apache.fop.layoutmgr.Position; |
49 |
import org.apache.fop.layoutmgr.PositionIterator; |
50 |
import org.apache.fop.layoutmgr.SpaceResolver; |
51 |
import org.apache.fop.layoutmgr.SubBreakerPosition; |
52 |
import org.apache.fop.layoutmgr.TraitSetter; |
53 |
import org.apache.fop.traits.MinOptMax; |
54 |
|
55 |
/** |
56 |
* This creates a single inline container area after |
57 |
* laying out the child block areas. All footnotes, floats |
58 |
* and id areas are maintained for later retrieval. |
59 |
*/ |
60 |
public class InlineContainerLayoutManager extends LeafNodeLayoutManager { |
61 |
|
62 |
Viewport currentViewport; |
63 |
InlineArea referenceArea; |
64 |
Block baseBlockArea; |
65 |
|
66 |
/** The Common Border, Padding and Background properties */ |
67 |
private CommonBorderPaddingBackground borderProps = null; |
68 |
/** The alignment adjust property */ |
69 |
private Length alignmentAdjust; |
70 |
/** The alignment baseline property */ |
71 |
private int alignmentBaseline = EN_BASELINE; |
72 |
/** The baseline shift property */ |
73 |
private Length baselineShift; |
74 |
/** The dominant baseline property */ |
75 |
private int dominantBaseline; |
76 |
/** The line height property */ |
77 |
private SpaceProperty lineHeight; |
78 |
|
79 |
private Length height; |
80 |
private Length width; |
81 |
private boolean autoHeight; |
82 |
private boolean autoWidth; |
83 |
|
84 |
private int contentAreaIPD; |
85 |
private int viewportContentBPD; |
86 |
private int referenceIPD; |
87 |
|
88 |
private FODimension relDims; |
89 |
|
90 |
private LayoutManager lastChildLM = null; |
91 |
|
92 |
|
93 |
/** |
94 |
* Construct a new InlineContainerLayoutManager |
95 |
* for the the given {@link InlineContainer} |
96 |
* |
97 |
* @param node the base {@link InlineContainer} |
98 |
*/ |
99 |
public InlineContainerLayoutManager(InlineContainer node) { |
100 |
super(node); |
101 |
} |
102 |
|
103 |
/** {@inheritDoc} */ |
104 |
@Override |
105 |
public void initialize() { |
106 |
InlineContainer node = (InlineContainer) fobj; |
107 |
|
108 |
borderProps = node.getCommonBorderPaddingBackground(); |
109 |
|
110 |
alignmentAdjust = node.getAlignmentAdjust(); |
111 |
alignmentBaseline = node.getAlignmentBaseline(); |
112 |
baselineShift = node.getBaselineShift(); |
113 |
dominantBaseline = node.getDominantBaseline(); |
114 |
|
115 |
boolean rotated = (node.getReferenceOrientation() % 180 != 0); |
116 |
if (rotated) { |
117 |
height = node.getInlineProgressionDimension() |
118 |
.getOptimum(this).getLength(); |
119 |
width = node.getBlockProgressionDimension() |
120 |
.getOptimum(this).getLength(); |
121 |
} else { |
122 |
height = node.getBlockProgressionDimension() |
123 |
.getOptimum(this).getLength(); |
124 |
width = node.getInlineProgressionDimension() |
125 |
.getOptimum(this).getLength(); |
126 |
} |
127 |
|
128 |
} |
129 |
|
130 |
/** |
131 |
* Sets the IPD of the content area |
132 |
* @param contentAreaIPD the IPD of the content area |
133 |
*/ |
134 |
protected void setContentAreaIPD(int contentAreaIPD) { |
135 |
this.contentAreaIPD = contentAreaIPD; |
136 |
} |
137 |
|
138 |
/** {@inheritDoc} */ |
139 |
@Override |
140 |
public List getNextKnuthElements(LayoutContext context, int alignment) { |
141 |
|
142 |
InlineContainer ic = (InlineContainer) fobj; |
143 |
boolean rotated = (ic.getReferenceOrientation() % 180 != 0); |
144 |
autoHeight = false; |
145 |
int maxbpd = context.getStackLimitBP().getOpt(); |
146 |
referenceIPD = context.getRefIPD(); |
147 |
|
148 |
int allocBPD; |
149 |
if (height.getEnum() == EN_AUTO |
150 |
|| (!height.isAbsolute() && getAncestorBlockAreaBPD() <= 0)) { |
151 |
//auto height when height="auto" or "if that dimension is not specified explicitly |
152 |
//(i.e., it depends on content's block-progression-dimension)" (XSL 1.0, 7.14.1) |
153 |
allocBPD = 0; |
154 |
autoHeight = true; |
155 |
} else { |
156 |
allocBPD = height.getValue(this); //this is the content-height |
157 |
allocBPD += borderProps.getBPPaddingAndBorder(false, this); |
158 |
} |
159 |
if (width.getEnum() == EN_AUTO) { |
160 |
setContentAreaIPD(referenceIPD); |
161 |
autoWidth = true; |
162 |
} |
163 |
viewportContentBPD = allocBPD - borderProps.getBPPaddingAndBorder(false, this); |
164 |
|
165 |
//double contentRectOffsetX = 0; |
166 |
//contentRectOffsetX += ic.getCommonMarginInline().spaceStart.getLength().getValue(this); |
167 |
//double contentRectOffsetY = 0; |
168 |
//contentRectOffsetY += borderProps.getBorderBeforeWidth(false); |
169 |
//contentRectOffsetY += borderProps.getPaddingBefore(false, this); |
170 |
|
171 |
updateRelDims(); |
172 |
|
173 |
LinkedList<KnuthSequence> returnList = new LinkedList<KnuthSequence>(); |
174 |
KnuthSequence inlineSeq = new InlineKnuthSequence(); |
175 |
|
176 |
addKnuthElementsForBorderPaddingStart(inlineSeq); |
177 |
|
178 |
MinOptMax refIPD = MinOptMax.getInstance(autoWidth ? referenceIPD : width.getValue(this)); |
179 |
InlineContainerBreaker breaker = new InlineContainerBreaker(this, refIPD); |
180 |
breaker.doLayout(height.getValue(this), autoHeight); |
181 |
|
182 |
if (!breaker.isEmpty()) { |
183 |
if (autoHeight) { |
184 |
//Update content BPD now that it is known |
185 |
int newHeight = breaker.getTotalWidth(); |
186 |
if (rotated) { |
187 |
setContentAreaIPD(newHeight); |
188 |
} else { |
189 |
viewportContentBPD = newHeight; |
190 |
} |
191 |
updateRelDims(); |
192 |
} |
193 |
|
194 |
Position icPosition = new SubBreakerPosition(this, breaker); |
195 |
inlineSeq.add( |
196 |
new KnuthInlineBox( |
197 |
refIPD.getOpt(), |
198 |
makeAlignmentContext(context), |
199 |
notifyPos(icPosition), |
200 |
false)); |
201 |
} |
202 |
|
203 |
addKnuthElementsForBorderPaddingEnd(inlineSeq); |
204 |
|
205 |
returnList.add(inlineSeq); |
206 |
|
207 |
setFinished(true); |
208 |
|
209 |
return returnList; |
210 |
} |
211 |
|
212 |
/** {@inheritDoc} */ |
213 |
@Override |
214 |
public void addChildArea(Area childArea) { |
215 |
baseBlockArea.addBlock((Block) childArea); |
216 |
} |
217 |
|
218 |
/** {@inheritDoc} */ |
219 |
@Override |
220 |
public void addAreas(PositionIterator posIter, LayoutContext context) { |
221 |
|
222 |
LinkedList<Position> positionList = new LinkedList<Position>(); |
223 |
Position pos; |
224 |
LayoutManager lastLM = null; // last child LM in this iterator |
225 |
Position lastPos = null; |
226 |
SubBreakerPosition icPos = null; |
227 |
while (posIter.hasNext()) { |
228 |
pos = posIter.next(); |
229 |
Position innerPosition = pos; |
230 |
if (pos instanceof NonLeafPosition) { |
231 |
innerPosition = pos.getPosition(); |
232 |
} |
233 |
if (pos instanceof SubBreakerPosition) { |
234 |
if (icPos != null) { |
235 |
throw new IllegalStateException("Only one SubBreakerPosition allowed"); |
236 |
} |
237 |
icPos = (SubBreakerPosition) pos; |
238 |
} else if (innerPosition == null) { |
239 |
//ignore (probably a Position for a simple penalty between blocks) |
240 |
} else { |
241 |
// innerPosition was created by another LM |
242 |
positionList.add(innerPosition); |
243 |
lastLM = innerPosition.getLM(); |
244 |
lastPos = innerPosition; |
245 |
} |
246 |
} |
247 |
|
248 |
addId(); |
249 |
addMarkersToPage( |
250 |
true, |
251 |
true, |
252 |
lastPos == null || isLast(lastPos)); |
253 |
|
254 |
LayoutManager prevLM = null; |
255 |
|
256 |
if (icPos == null) { |
257 |
PositionIterator childPosIter |
258 |
= new PositionIterator(positionList.listIterator()); |
259 |
|
260 |
LayoutManager childLM; |
261 |
while ((childLM = childPosIter.getNextChildLM()) != null) { |
262 |
context.setFlags(LayoutContext.LAST_AREA, |
263 |
context.isLastArea() && childLM == lastLM); |
264 |
childLM.addAreas(childPosIter, context); |
265 |
context.setLeadingSpace(context.getTrailingSpace()); |
266 |
context.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true); |
267 |
prevLM = childLM; |
268 |
} |
269 |
|
270 |
currentViewport.setContent(referenceArea); |
271 |
getParent().addChildArea(currentViewport); |
272 |
} else { |
273 |
((AbstractContainerBreaker)icPos.getBreaker()).addContainedAreas(); |
274 |
} |
275 |
|
276 |
addMarkersToPage( |
277 |
false, |
278 |
true, |
279 |
lastPos == null || isLast(lastPos)); |
280 |
|
281 |
boolean isLast = (context.isLastArea() && prevLM == lastChildLM); |
282 |
context.setFlags(LayoutContext.LAST_AREA, isLast); |
283 |
} |
284 |
|
285 |
/** {@inheritDoc} */ |
286 |
@Override |
287 |
public int getContentAreaIPD() { |
288 |
return width.getEnum() == EN_AUTO |
289 |
? contentAreaIPD |
290 |
: width.getValue(this); |
291 |
} |
292 |
|
293 |
/** {@inheritDoc} */ |
294 |
@Override |
295 |
public int getContentAreaBPD() { |
296 |
return height.getEnum() == EN_AUTO |
297 |
? viewportContentBPD |
298 |
: height.getValue(this); |
299 |
} |
300 |
|
301 |
/** |
302 |
* Get the parent area for children of this inline-container. |
303 |
* This returns the current inline-container area |
304 |
* and creates it if required. |
305 |
* |
306 |
* {@inheritDoc} |
307 |
*/ |
308 |
@Override |
309 |
public Area getParentArea(Area childArea) { |
310 |
if (referenceArea == null) { |
311 |
currentViewport = new Viewport(childArea); |
312 |
currentViewport.addTrait(Trait.IS_VIEWPORT_AREA, Boolean.TRUE); |
313 |
currentViewport.setIPD(getContentAreaIPD()); |
314 |
currentViewport.setBPD(getContentAreaBPD()); |
315 |
|
316 |
TraitSetter.setProducerID(currentViewport, fobj.getId()); |
317 |
TraitSetter.addBorders(currentViewport, |
318 |
borderProps, |
319 |
false, false, false, false, this); |
320 |
TraitSetter.addPadding(currentViewport, |
321 |
borderProps, |
322 |
false, false, false, false, this); |
323 |
TraitSetter.addBackground(currentViewport, |
324 |
borderProps, |
325 |
this); |
326 |
|
327 |
//currentViewport.setCTM(relativeCTM); |
328 |
currentViewport.setClip(needClip()); |
329 |
currentViewport.setContentPosition( |
330 |
new java.awt.geom.Rectangle2D.Float(0, 0, getContentAreaIPD(), getContentAreaBPD())); |
331 |
referenceArea = new InlineBlockParent(); |
332 |
referenceArea.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE); |
333 |
TraitSetter.setProducerID(referenceArea, fobj.getId()); |
334 |
// Set up dimensions |
335 |
// Must get dimensions from parent area |
336 |
getParent().getParentArea(referenceArea); |
337 |
referenceArea.setIPD(referenceIPD); |
338 |
baseBlockArea = new Block(); |
339 |
referenceArea.addChildArea(baseBlockArea); |
340 |
// Get reference IPD from parentArea |
341 |
setCurrentArea(currentViewport); // ??? for generic operations |
342 |
} |
343 |
return referenceArea; |
344 |
} |
345 |
|
346 |
private boolean needClip() { |
347 |
int overflow = ((InlineContainer) fobj).getOverflow(); |
348 |
return (overflow == EN_HIDDEN || overflow == EN_ERROR_IF_OVERFLOW); |
349 |
} |
350 |
|
351 |
/** {@inheritDoc} */ |
352 |
@Override |
353 |
protected AlignmentContext makeAlignmentContext(LayoutContext context) { |
354 |
return new AlignmentContext( |
355 |
viewportContentBPD |
356 |
, alignmentAdjust |
357 |
, alignmentBaseline |
358 |
, baselineShift |
359 |
, dominantBaseline |
360 |
, context.getAlignmentContext() |
361 |
); |
362 |
} |
363 |
|
364 |
/** |
365 |
* Get the allocation ipd of the inline area. |
366 |
* This method may be overridden to handle percentage values. |
367 |
* @param refIPD the ipd of the parent reference area |
368 |
* @return the min/opt/max ipd of the inline area |
369 |
*/ |
370 |
@Override |
371 |
protected MinOptMax getAllocationIPD(int refIPD) { |
372 |
return MinOptMax.getInstance(curArea.getIPD()); |
373 |
} |
374 |
|
375 |
/** |
376 |
* "wrap" the Position inside each element moving the elements from |
377 |
* SourceList to targetList |
378 |
* @param sourceList source list |
379 |
* @param targetList target list receiving the wrapped position elements |
380 |
*/ |
381 |
private void wrapPositionElements(List sourceList, List targetList) { |
382 |
wrapPositionElements(sourceList, targetList, false); |
383 |
} |
384 |
|
385 |
/** |
386 |
* "wrap" the Position inside each element moving the elements from |
387 |
* SourceList to targetList |
388 |
* @param sourceList source list |
389 |
* @param targetList target list receiving the wrapped position elements |
390 |
* @param force if true, every Position is wrapped regardless of its LM of origin |
391 |
*/ |
392 |
private void wrapPositionElements(List sourceList, List targetList, boolean force) { |
393 |
|
394 |
ListIterator listIter = sourceList.listIterator(); |
395 |
Object tempElement; |
396 |
while (listIter.hasNext()) { |
397 |
tempElement = listIter.next(); |
398 |
if (tempElement instanceof ListElement) { |
399 |
wrapPositionElement( |
400 |
(ListElement) tempElement, |
401 |
targetList, |
402 |
force); |
403 |
} else if (tempElement instanceof List) { |
404 |
wrapPositionElements( |
405 |
(List) tempElement, |
406 |
targetList, |
407 |
force); |
408 |
} |
409 |
} |
410 |
} |
411 |
|
412 |
/** |
413 |
* "wrap" the Position inside the given element and add it to the target list. |
414 |
* @param el the list element |
415 |
* @param targetList target list receiving the wrapped position elements |
416 |
* @param force if true, every Position is wrapped regardless of its LM of origin |
417 |
*/ |
418 |
private void wrapPositionElement(ListElement el, List targetList, boolean force) { |
419 |
if (force || el.getLayoutManager() != this) { |
420 |
el.setPosition(notifyPos(new NonLeafPosition(this, |
421 |
el.getPosition()))); |
422 |
} |
423 |
targetList.add(el); |
424 |
} |
425 |
|
426 |
private void updateRelDims() { |
427 |
relDims = new FODimension(0, 0); |
428 |
/* |
429 |
CTM relativeCTM = CTM.getCTMandRelDims( |
430 |
((InlineContainer) fobj).getReferenceOrientation(), |
431 |
((InlineContainer) fobj).getWritingMode(), |
432 |
rect, relDims); |
433 |
*/ |
434 |
} |
435 |
|
436 |
private class InlineContainerBreaker extends AbstractContainerBreaker { |
437 |
|
438 |
InlineContainerBreaker(InlineContainerLayoutManager iclm, MinOptMax ipd) { |
439 |
super(iclm, ipd); |
440 |
} |
441 |
|
442 |
/** {@inheritDoc} */ |
443 |
@Override |
444 |
public boolean isOverflow() { |
445 |
return !isEmpty() |
446 |
&& (deferredAlg.getPageBreaks().size() > 1); |
447 |
//|| (deferredAlg.getOverflowAmount() > 0)); |
448 |
} |
449 |
|
450 |
/** {@inheritDoc} */ |
451 |
@Override |
452 |
public int getOverflowAmount() { |
453 |
return 0; //deferredAlg.getOverflowAmount(); |
454 |
} |
455 |
|
456 |
/** {@inheritDoc}Ê*/ |
457 |
@Override |
458 |
protected LayoutContext createLayoutContext() { |
459 |
LayoutContext lc = super.createLayoutContext(); |
460 |
lc.setWritingMode(((InlineContainer) fobj).getWritingMode()); |
461 |
return lc; |
462 |
} |
463 |
|
464 |
/** {@inheritDoc} */ |
465 |
protected List<KnuthElement> getNextKnuthElements(LayoutContext context, int alignment) { |
466 |
LayoutManager curLM; // currently active LM |
467 |
List<KnuthElement> returnList = new LinkedList<KnuthElement>(); |
468 |
|
469 |
while ((curLM = getChildLM()) != null) { |
470 |
LayoutContext childLC = new LayoutContext(0); |
471 |
childLC.setStackLimitBP(context.getStackLimitBP()); |
472 |
childLC.setRefIPD(context.getRefIPD()); |
473 |
childLC.setWritingMode(((InlineContainer)fobj).getWritingMode()); |
474 |
|
475 |
List<ListElement> returnedList = null; |
476 |
if (!curLM.isFinished()) { |
477 |
returnedList = curLM.getNextKnuthElements(childLC, alignment); |
478 |
} |
479 |
if (returnedList != null) { |
480 |
wrapPositionElements(returnedList, returnList); |
481 |
} |
482 |
} |
483 |
SpaceResolver.resolveElementList(returnList); |
484 |
setFinished(true); |
485 |
return returnList; |
486 |
} |
487 |
|
488 |
/** {@inheritDoc} */ |
489 |
@Override |
490 |
protected void addAreas(PositionIterator posIter, LayoutContext context) { |
491 |
getTopLevelLM().addAreas(posIter, context); |
492 |
} |
493 |
|
494 |
/** {@inheritDoc}Ê*/ |
495 |
@Override |
496 |
protected int getCurrentDisplayAlign() { |
497 |
return ((InlineContainer)fobj).getDisplayAlign(); |
498 |
} |
499 |
} |
500 |
|
501 |
} |
0 |
+ Id |
502 |
+ Id |
1 |
+ native |
503 |
+ native |