2026-04-10 11:46:11 +02:00
|
|
|
import { Handler } from '/csspageweaver/lib/paged.esm.js';
|
|
|
|
|
|
|
|
|
|
export default class snapToBaseline extends Handler {
|
|
|
|
|
constructor(chunker, polisher, caller) {
|
|
|
|
|
super(chunker, polisher, caller);
|
2026-04-10 12:51:41 +02:00
|
|
|
this.baseline;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
beforeParsed(content){
|
|
|
|
|
this.baseline = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--baseline').trim());
|
2026-04-14 18:18:10 +02:00
|
|
|
|
|
|
|
|
content.querySelectorAll('blockquote').forEach((bq) => {
|
|
|
|
|
const prev = bq.previousElementSibling;
|
|
|
|
|
if (!prev || prev.nodeName !== 'P') {
|
|
|
|
|
bq.style.color = 'red';
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-04-10 11:46:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
renderNode(node, sourceNode){
|
|
|
|
|
if (node.nodeName === 'P') {
|
2026-04-10 12:51:41 +02:00
|
|
|
this.baseline = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--baseline').trim());
|
2026-04-14 18:18:10 +02:00
|
|
|
if (!shouldSnap(node)) return node;
|
2026-04-10 12:51:41 +02:00
|
|
|
const area = node.closest('.pagedjs_area');
|
|
|
|
|
if (area) {
|
|
|
|
|
const areaRect = area.getBoundingClientRect();
|
2026-04-15 11:08:34 +02:00
|
|
|
// getClientRects()[0] = premier fragment (colonne 1 si le paragraphe
|
|
|
|
|
// s'étend sur 2 colonnes). getBoundingClientRect().top retournerait
|
|
|
|
|
// le minimum des deux tops, soit le haut de la colonne 2 — incorrect.
|
|
|
|
|
const firstRect = node.getClientRects()[0];
|
|
|
|
|
if (!firstRect) return node;
|
|
|
|
|
const relativeTop = firstRect.top - areaRect.top;
|
2026-04-10 12:51:41 +02:00
|
|
|
const modulo = relativeTop % this.baseline;
|
2026-04-15 11:08:34 +02:00
|
|
|
|
2026-04-10 12:51:41 +02:00
|
|
|
if (modulo !== 0) {
|
2026-04-14 18:18:10 +02:00
|
|
|
node.style.paddingTop = (this.baseline - modulo) + 'px';
|
2026-04-15 11:08:34 +02:00
|
|
|
node.style.color = "green";
|
2026-04-10 12:51:41 +02:00
|
|
|
}
|
|
|
|
|
}
|
2026-04-10 11:46:11 +02:00
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-14 18:18:10 +02:00
|
|
|
afterPageLayout(pageElement, page, breakToken){
|
|
|
|
|
const area = pageElement.querySelector('.pagedjs_area');
|
|
|
|
|
if (!area) return;
|
|
|
|
|
|
|
|
|
|
const areaRect = area.getBoundingClientRect();
|
|
|
|
|
const paragraphs = pageElement.querySelectorAll('p');
|
|
|
|
|
|
|
|
|
|
paragraphs.forEach((node) => {
|
|
|
|
|
if (!shouldSnap(node)) return;
|
|
|
|
|
|
2026-04-15 11:08:34 +02:00
|
|
|
const firstRect = node.getClientRects()[0];
|
|
|
|
|
if (!firstRect) return;
|
|
|
|
|
|
|
|
|
|
// firstRect.bottom = bas du premier fragment (colonne 1 pour un paragraphe
|
|
|
|
|
// qui s'étend sur 2 colonnes). Cela permet de cibler correctement les
|
|
|
|
|
// paragraphes dont la première partie touche le bas de la colonne.
|
|
|
|
|
if (firstRect.bottom < areaRect.bottom - this.baseline) return;
|
|
|
|
|
|
|
|
|
|
const relativeTop = firstRect.top - areaRect.top;
|
2026-04-14 18:18:10 +02:00
|
|
|
const modulo = relativeTop % this.baseline;
|
|
|
|
|
if (modulo !== 0) {
|
|
|
|
|
node.style.paddingTop = (this.baseline - modulo) + 'px';
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
}
|
2026-04-10 11:46:11 +02:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2026-04-14 18:18:10 +02:00
|
|
|
function shouldSnap(node) {
|
|
|
|
|
return !node.closest('blockquote');
|
|
|
|
|
}
|