[
https://issues.apache.org/jira/browse/FOP-3334?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Martin Leitner updated FOP-3334:
--------------------------------
Attachment: minwidth-zero-viewport.fo
areatree-fop-2.11-stock.xml
areatree-fop-2.11-patched.xml
minwidth-zero-viewport-fop-2.11.pdf
> min-width/min-height on fo:external-graphic is used as the fixed viewport
> size, so min-width="0" collapses the viewport and graphics overlap
> --------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: FOP-3334
> URL: https://issues.apache.org/jira/browse/FOP-3334
> Project: FOP
> Issue Type: Bug
> Components: layout/inline
> Affects Versions: 2.11, main
> Environment: Apache FOP 2.11 official binary distribution (SHA-512
> verified), OpenJDK 21.0.2, Windows 11
> Reporter: Martin Leitner
> Priority: Major
> Attachments: areatree-fop-2.11-patched.xml,
> areatree-fop-2.11-stock.xml, minwidth-zero-viewport-fop-2.11.pdf,
> minwidth-zero-viewport.fo
>
>
> h3. Summary
> An explicit {{min-width}} / {{min-height}} (or the equivalent
> {{inline-progression-dimension.minimum}} /
> {{block-progression-dimension.minimum}}) on {{fo:external-graphic}} is used
> as the *fixed* viewport size instead of as a lower bound. With
> {{min-width="0"}} the viewport becomes 0 wide while the image content keeps
> its full width, so consecutive graphics are all painted at the same position
> and overlap. {{min-height="0"}} does the same in the block-progression
> direction.
> Since {{0}} is the initial value of {{min-width}} in XSL-FO 1.1, any tool
> that writes the property out explicitly triggers this.
> h3. Steps to reproduce
> Attached {{minwidth-zero-viewport.fo}} is fully self-contained (the three
> images are RFC 2397 {{data:}} URIs, so no external resources are needed).
> {code}
> fop -fo minwidth-zero-viewport.fo -pdf out.pdf
> fop -fo minwidth-zero-viewport.fo -at application/pdf out-at.xml
> {code}
> Block *A* draws three graphics (120x60, 90x60, 60x60 px) with no
> {{min-width}}; block *B* draws the same three with {{min-width="0"}}.
> h3. Actual result (Apache FOP 2.11, official binary distribution)
> Viewport extents from the area tree, in millipoints:
> || id || min-* set || viewport ipd || viewport bpd || content pos ||
> | a-red | - | 120000 | 60000 | 0 0 120000 60000 |
> | a-green | - | 90000 | 60000 | 0 0 90000 60000 |
> | a-blue | - | 60000 | 60000 | 0 0 60000 60000 |
> | b-red | min-width="0" | *0* | 60000 | 0 0 120000 60000 |
> | b-green | min-width="0" | *0* | 60000 | 0 0 90000 60000 |
> | b-blue | min-width="0" | *0* | 60000 | 0 0 60000 60000 |
> | c-red | min-width="3in" | 216000 | 60000 | 0 0 120000 60000 |
> | d-red | min-height="0" | 120000 | *0* | 0 0 120000 60000 |
> | d-green | min-height="0" | 90000 | *0* | 0 0 90000 60000 |
> | d-blue | min-height="0" | 60000 | *0* | 0 0 60000 60000 |
> The {{b-*}} rows are the defect: the viewport is reported as {{ipd="0"}}
> while its content placement is the full {{120000}} / {{90000}} / {{60000}}
> wide image:
> {code:xml}
> <viewport ipd="0" bpd="60000" prod-id="b-red" pos="0 0 120000 60000">
> <image prod-id="b-red" url="data:image/png;base64,..."/>
> </viewport>
> {code}
> Because each viewport advances the inline-progression position by 0, all
> three images are painted on top of each other in the PDF. The {{c-red}} row
> shows that a *non-zero* minimum does widen the viewport as expected, so only
> the zero case is visibly broken - but the underlying cause is the same for
> every explicit minimum.
> h3. Expected result
> A minimum is a lower bound, not the size. The viewport should be sized from
> the content and then constrained by the minimum/maximum, i.e. {{max(content
> extent, minimum)}} - which for {{min-width="0"}} yields the content width and
> reproduces the same output as if the property were absent.
> h3. Cause
> {{min-width}} / {{min-height}} are not read directly. They are _extra
> corresponding_ properties of {{inline-progression-dimension}} /
> {{block-progression-dimension}} ({{FOPropertyMapping}}, the
> {{setExtraCorresponding()}} calls for both dimension properties), and
> {{DimensionPropertyMaker.compute()}} copies an explicit value into the
> {{CP_MINIMUM}} subproperty of the length-range.
> {{ImageLayout.doLayout()}} then takes that minimum as the viewport extent
> (2.11 lines 89-93, and 80-83 for the BPD):
> {code:java}
> len =
> props.getInlineProgressionDimension().getMinimum(percentBaseContext).getLength();
> if (ipd == -1 && len.getEnum() != EN_AUTO) {
> //Establish minimum viewport size
> ipd = len.getValue(percentBaseContext);
> }
> {code}
> Because {{ipd}} is no longer {{-1}}, the content-driven sizing at lines
> 163-171 is skipped:
> {code:java}
> //Adjust viewport if not explicit
> if (ipd == -1) {
> ipd = constrainExtent(cwidth,
> props.getInlineProgressionDimension(), props.getContentWidth());
> }
> {code}
> {{constrainExtent()}} is where the correct semantics already live - it
> applies the maximum as a ceiling and the minimum as a floor ({{extent =
> Math.max(extent, min)}}) - but it is only reached when no minimum was
> specified.
> h3. Suggested fix
> Keep the provisional minimum-derived extent (the
> {{content-width}}/{{content-height}} {{scale-*-to-fit}} branches rely on it)
> but re-derive the final viewport extent through {{constrainExtent()}}:
> {code:java}
> int bpd = -1;
> int ipd = -1;
> boolean bpdFromMinimum = false;
> boolean ipdFromMinimum = false;
> ...
> // where the minimum is currently assigned, also set the corresponding flag
> ...
> //Adjust viewport if not explicit
> if (ipd == -1 || ipdFromMinimum) {
> ipd = constrainExtent(cwidth,
> props.getInlineProgressionDimension(), props.getContentWidth());
> }
> if (bpd == -1 || bpdFromMinimum) {
> bpd = constrainExtent(cheight,
> props.getBlockProgressionDimension(), props.getContentHeight());
> }
> {code}
> No change is needed in {{DimensionPropertyMaker}} or {{FOPropertyMapping}};
> the property-to-subproperty mapping is correct, only the layout consumption
> is wrong.
> *Verified against the official 2.11 binary distribution* by compiling only
> this patched class and putting it ahead of {{fop-core-2.11.jar}} on the
> classpath. Attached {{areatree-fop-2.11-patched.xml}} is the result:
> * {{b-red}} / {{b-green}} / {{b-blue}} viewport ipd: {{0}} -> {{120000}} /
> {{90000}} / {{60000}} (equal to the {{a-*}} reference, no overlap)
> * {{d-*}} viewport bpd: {{0}} -> {{60000}}
> * {{c-red}} (min-width="3in") unchanged at {{216000}} - the floor still
> applies
> * {{a-*}} (no minimum) unchanged
> A side-case matrix produced identical area trees before and after the patch
> for: {{inline-progression-dimension.minimum}} at 1in and 3in combined with
> {{content-width="scale-to-fit"}}, {{scale-down-to-fit}} and
> {{scale-up-to-fit}}; {{block-progression-dimension.minimum}} with
> {{content-height="scale-to-fit"}}; an explicit {{.optimum}}; and a
> conflicting {{min-width="4in" max-width="2in"}} pair. In particular the
> {{external-graphic_size_1}} layout-engine testcase pattern
> ({{inline-progression-dimension.minimum="1in"}} +
> {{content-width="scale-to-fit"}}) is unaffected.
> h3. Related observation (not addressed by the patch above)
> {{min-width="0"}} combined with {{content-width="scale-to-fit"}} makes the
> graphic disappear completely - no viewport or image area is generated at all
> - both before and after the patch. The content extent is scaled to the
> provisional zero viewport before the viewport is re-derived. This may deserve
> separate treatment; it is not the overlap defect reported here.
> h3. Versions checked
> * 2.11 (latest release): reproduced by execution; {{ImageLayout}} lines 89-93
> and 163-171 as quoted above.
> * 2.4, 2.8, 2.10: the same block is present in the published sources jars.
> * trunk HEAD (gitbox): the same block is present (shifted to lines ~95-97 by
> the unrelated {{useParentIPDImageScaling}} addition).
> h3. Environment
> Apache FOP 2.11 binary distribution (SHA-512 verified against the published
> checksum), OpenJDK 21.0.2, Windows 11.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)