Skip to content

Commit 08bd76d

Browse files
committed
XFA - Fix layout issues
- PR #13554 is buggy, so this patch aims to fix bugs. - check if a component fits into its parent in taking into account the parent layout. - introduce method isSplittable for template nodes to know if a component can be splitted in case of overflow.
1 parent f9a0568 commit 08bd76d

File tree

6 files changed

+307
-214
lines changed

6 files changed

+307
-214
lines changed

src/core/xfa/html_utils.js

+3-10
Original file line numberDiff line numberDiff line change
@@ -337,16 +337,9 @@ function fixDimensions(node) {
337337
}
338338
}
339339

340-
if (node.layout === "position") {
341-
// Acrobat doesn't take into account min, max values
342-
// for containers with positioned layout (which makes sense).
343-
node.minW = node.minH = 0;
344-
node.maxW = node.maxH = Infinity;
345-
} else {
346-
if (node.layout === "table") {
347-
if (node.w === "" && Array.isArray(node.columnWidths)) {
348-
node.w = node.columnWidths.reduce((a, x) => a + x, 0);
349-
}
340+
if (node.layout === "table") {
341+
if (node.w === "" && Array.isArray(node.columnWidths)) {
342+
node.w = node.columnWidths.reduce((a, x) => a + x, 0);
350343
}
351344
}
352345
}

src/core/xfa/layout.js

+167-39
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,13 @@
1313
* limitations under the License.
1414
*/
1515

16-
import { $extra, $flushHTML } from "./xfa_object.js";
16+
import {
17+
$extra,
18+
$flushHTML,
19+
$getParent,
20+
$getTemplateRoot,
21+
$isSplittable,
22+
} from "./xfa_object.js";
1723
import { measureToString } from "./html_utils.js";
1824

1925
// Subform and ExclGroup have a layout so they share these functions.
@@ -146,59 +152,181 @@ function addHTML(node, html, bbox) {
146152

147153
function getAvailableSpace(node) {
148154
const availableSpace = node[$extra].availableSpace;
149-
const marginH = node.margin
155+
const marginV = node.margin
150156
? node.margin.topInset + node.margin.bottomInset
151157
: 0;
158+
const marginH = node.margin
159+
? node.margin.leftInset + node.margin.rightInset
160+
: 0;
152161

153162
switch (node.layout) {
154163
case "lr-tb":
155164
case "rl-tb":
156-
switch (node[$extra].attempt) {
157-
case 0:
158-
return {
159-
width: availableSpace.width - node[$extra].currentWidth,
160-
height: availableSpace.height - marginH - node[$extra].prevHeight,
161-
};
162-
case 1:
163-
return {
164-
width: availableSpace.width,
165-
height: availableSpace.height - marginH - node[$extra].height,
166-
};
167-
default:
168-
// Overflow must stay in the container.
169-
return {
170-
width: Infinity,
171-
height: Infinity,
172-
};
165+
if (node[$extra].attempt === 0) {
166+
return {
167+
width: availableSpace.width - marginH - node[$extra].currentWidth,
168+
height: availableSpace.height - marginV - node[$extra].prevHeight,
169+
};
173170
}
171+
return {
172+
width: availableSpace.width - marginH,
173+
height: availableSpace.height - marginV - node[$extra].height,
174+
};
174175
case "rl-row":
175176
case "row":
176-
if (node[$extra].attempt === 0) {
177-
const width = node[$extra].columnWidths
178-
.slice(node[$extra].currentColumn)
179-
.reduce((a, x) => a + x);
180-
return { width, height: availableSpace.height - marginH };
177+
const width = node[$extra].columnWidths
178+
.slice(node[$extra].currentColumn)
179+
.reduce((a, x) => a + x);
180+
return { width, height: availableSpace.height - marginH };
181+
case "table":
182+
case "tb":
183+
return {
184+
width: availableSpace.width - marginH,
185+
height: availableSpace.height - marginV - node[$extra].height,
186+
};
187+
case "position":
188+
default:
189+
return availableSpace;
190+
}
191+
}
192+
193+
function getTransformedBBox(node) {
194+
// Take into account rotation and anchor the get the
195+
// real bounding box.
196+
let w = node.w === "" ? NaN : node.w;
197+
let h = node.h === "" ? NaN : node.h;
198+
let [centerX, centerY] = [0, 0];
199+
switch (node.anchorType || "") {
200+
case "bottomCenter":
201+
[centerX, centerY] = [w / 2, h];
202+
break;
203+
case "bottomLeft":
204+
[centerX, centerY] = [0, h];
205+
break;
206+
case "bottomRight":
207+
[centerX, centerY] = [w, h];
208+
break;
209+
case "middleCenter":
210+
[centerX, centerY] = [w / 2, h / 2];
211+
break;
212+
case "middleLeft":
213+
[centerX, centerY] = [0, h / 2];
214+
break;
215+
case "middleRight":
216+
[centerX, centerY] = [w, h / 2];
217+
break;
218+
case "topCenter":
219+
[centerX, centerY] = [w / 2, 0];
220+
break;
221+
case "topRight":
222+
[centerX, centerY] = [w, 0];
223+
break;
224+
}
225+
226+
let x;
227+
let y;
228+
switch (node.rotate || 0) {
229+
case 0:
230+
[x, y] = [-centerX, -centerY];
231+
break;
232+
case 90:
233+
[x, y] = [-centerY, centerX];
234+
[w, h] = [h, -w];
235+
break;
236+
case 180:
237+
[x, y] = [centerX, centerY];
238+
[w, h] = [-w, -h];
239+
break;
240+
case 270:
241+
[x, y] = [centerY, -centerX];
242+
[w, h] = [-h, w];
243+
break;
244+
}
245+
246+
return [
247+
node.x + x + Math.min(0, w),
248+
node.y + y + Math.min(0, h),
249+
Math.abs(w),
250+
Math.abs(h),
251+
];
252+
}
253+
254+
/**
255+
* Returning true means that the node will be layed out
256+
* else the layout will go to its next step (changing of line
257+
* in case of lr-tb or changing content area...).
258+
*/
259+
function checkDimensions(node, space) {
260+
if (node.w === 0 || node.h === 0) {
261+
return true;
262+
}
263+
264+
if (space.width <= 0 || space.height <= 0) {
265+
return false;
266+
}
267+
268+
const parent = node[$getParent]();
269+
const attempt = (node[$extra] && node[$extra].attempt) || 0;
270+
switch (parent.layout) {
271+
case "lr-tb":
272+
case "rl-tb":
273+
switch (attempt) {
274+
case 0: {
275+
let w, h;
276+
if (node.w !== "" || node.h !== "") {
277+
[, , w, h] = getTransformedBBox(node);
278+
}
279+
if (node.h !== "" && Math.round(h - space.height) > 1) {
280+
return false;
281+
}
282+
if (node.w !== "") {
283+
return Math.round(w - space.width) <= 1;
284+
}
285+
286+
return node.minW <= space.width;
287+
}
288+
case 1: {
289+
if (node.h !== "" && !node[$isSplittable]()) {
290+
const [, , , h] = getTransformedBBox(node);
291+
if (Math.round(h - space.height) > 1) {
292+
return false;
293+
}
294+
}
295+
return true;
296+
}
297+
default:
298+
return true;
181299
}
182-
// Overflow must stay in the container.
183-
return { width: Infinity, height: Infinity };
184300
case "table":
185301
case "tb":
186-
if (node[$extra].attempt === 0) {
187-
return {
188-
width: availableSpace.width,
189-
height: availableSpace.height - marginH - node[$extra].height,
190-
};
302+
if (attempt !== 1 && node.h !== "" && !node[$isSplittable]()) {
303+
const [, , , h] = getTransformedBBox(node);
304+
if (Math.round(h - space.height) > 1) {
305+
return false;
306+
}
191307
}
192-
// Overflow must stay in the container.
193-
return { width: Infinity, height: Infinity };
308+
return true;
194309
case "position":
195-
default:
196-
if (node[$extra].attempt === 0) {
197-
return availableSpace;
310+
const [x, y, w, h] = getTransformedBBox(node);
311+
const isWidthOk = node.w === "" || Math.round(w + x - space.width) <= 1;
312+
const isHeightOk = node.h === "" || Math.round(h + y - space.height) <= 1;
313+
314+
if (isWidthOk && isHeightOk) {
315+
return true;
198316
}
199-
// Overflow must stay in the container.
200-
return { width: Infinity, height: Infinity };
317+
318+
const area = node[$getTemplateRoot]()[$extra].currentContentArea;
319+
if (isWidthOk) {
320+
return h + y > area.h;
321+
}
322+
323+
return w + x > area.w;
324+
case "rl-row":
325+
case "row":
326+
default:
327+
// No layout, so accept everything.
328+
return true;
201329
}
202330
}
203331

204-
export { addHTML, flushHTML, getAvailableSpace };
332+
export { addHTML, checkDimensions, flushHTML, getAvailableSpace };

0 commit comments

Comments
 (0)