Forum How do I...?

Bilingual layout

mcmeeking
Hi there,

I'm struggling to get Prince to collapse the margins on my bilingual documents and could use some assistance.

The content is a faux two column layout, structured like so:

<div class="bilingual-content">
  <div class="language-column" lang="en" dir="ltr">
    <h1 data-block-key="1">This is a heading</h1>
    <h2 data-block-key="2">And a subheading</h2>
    <p data-block-key="3">And some text.</p>
  </div>
  <div class="language-column" lang="ar" dir="rtl">
    <h1 data-block-key="1">هذا هو العنوان</h1>
    <h2 data-block-key="2">والعنوان الفرعي</h2>
    <p data-block-key="3">وبعض النص.</p>
  </div>
</div>


Initially I was using a flex layout:

.bilingual-content {
  padding: 0mm;
  margin: 0mm;
  display: flex;
}

.language-column {
  padding: 0mm;
  margin: 0mm;
  flex: 1;
  display: block;
}


But have since learned in the docs that the margins are not collapsed in flex layouts, so have switched to using:

.bilingual-content {
  display: table;
}

.language-column {
  display: table-cell;
  width: 50%;
}


My issue is that Prince is still not collapsing the top-margin for elements on the start of a new page, so there is a significant top margin on page's first child which would ordinarily be "chomped".

Is there something I am doing wrong here, or can anyone suggest a method of collapsing the top margin for page first-child elements?

I've attached screenshots of the margin of the page/column first-child elements for reference.
  1. bilingual.png70.4 kB
    Bilingual with incorrect top-margin
  2. monolingual.png59.7 kB
    Monolingual with correct top-margin
howcome
Indeed, Prince will not collapse top margins when the element is inside a table cell.

The simplest solution is to set the top margin of the first element in a table to zero. E.g., if all cells have a h1 heading:

td h1:first-child { margin-top: 0 }


Then you would add some margin at the bottom of the cell to keep some distance between elements.

If you absolutely need to set style on an element on a certain page, you can use the technique described here:

https://css4.pub/2023/boxtracking/#color-me-red-but-only-on-page-2-please

Edited by howcome

mcmeeking
Thanks for the reply but I think I may have confused things by including my switch to table layout - there was no appreciable difference between flex layout (flex columns) and table layout as far as margin-top collapsing (as you noted in your reply).

I did figure this out in the end by simply brute-forcing the margin-top for the first elements on each page using JS and the multi-pass guidance in the docs.

Just in case anyone else ever has this niche problem, here's the script I used:

// Script to perform transformations to the bilingual document layout:
// - Set the min-height of each bilingual element pair (with the same data-block-key) to the height of the tallest element in the pair.
// - Set the margin-top of the first bilingual element pair on each page to 0 (recurses per page in the document).
Prince.trackBoxes = true;
Prince.registerPostLayoutFunc(processLayout);

// Global variable to track the current page being processed.
var currentPageBeingProcessed = 0;

function processLayout() {
  var elements = document.querySelectorAll('[data-block-key]');

  // Increment the global page counter at the start of each call.
  currentPageBeingProcessed++;

  if (currentPageBeingProcessed === 1) {
    // Set the uniform heights only on the first pass.
    setUniformHeights(elements);
  }

  console.log("Processing page " + currentPageBeingProcessed + "...");

  if (currentPageBeingProcessed <= Prince.pageCount) {
    // Adjust margin-top for the first bilingual element pair on the current page.
    setMarginTop(elements, currentPageBeingProcessed);
    console.log("Page " + currentPageBeingProcessed + " processed.");

    // Re-register the callback for the next page if we haven't processed all pages yet.
    Prince.registerPostLayoutFunc(processLayout);
  } else {
    // Reset or perform any final actions if needed when all pages have been processed.
    currentPageBeingProcessed = 0;
    console.log("All pages processed.");
  }
}

function setUniformHeights(elements) {
  var keys = {};

  // Store keys to identify unique content blocks
  for (var i = 0; i < elements.length; i++) {
    var key = elements[i].getAttribute('data-block-key');
    keys[key] = true;
  }

  // Adjust heights for each key
  for (var key in keys) {
    if (keys.hasOwnProperty(key)) {
      var elementsWithSameKey = document.querySelectorAll('[data-block-key="' + key + '"]');
      var maxHeight = 0;

      // Find the max height of the element pair
      for (var j = 0; j < elementsWithSameKey.length; j++) {
        var element = elementsWithSameKey[j];
        var boxes = element.getPrinceBoxes();

        if (boxes.length > 0) {
          var height = boxes[0].h;
          maxHeight = Math.max(maxHeight, height);
        }
      }

      // Apply the max height to all elements with the same key
      for (var j = 0; j < elementsWithSameKey.length; j++) {
        elementsWithSameKey[j].style.minHeight = maxHeight + "pt";
      }
    }
  }
}

function setMarginTop(elements, pageNum) {
  // This function sets the first bilingual element pair on the current page to margin-top: 0.
  for (var i = 0; i < elements.length; i++) {
    var element = elements[i];
    var boxes = element.getPrinceBoxes();

    if (boxes.length > 0 && boxes[0].pageNum === pageNum) {
      elementsWithSameKey = document.querySelectorAll('[data-block-key="' + element.getAttribute('data-block-key') + '"]');
      for (var j = 0; j < elementsWithSameKey.length; j++) {
        elementsWithSameKey[j].style.marginTop = "0";
      }
      return;
    }
  }
};