<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Introspection Sample</title>
    <link href="../common.css" rel="stylesheet" type="text/css" />
    <style>
        html {
            hyphens: auto;
            hyphenate-before: 3;
            hyphenate-after: 3;
        }
    </style>
</head>

<body>
    <h1>Access to Layout Information with JavaScript</h1>
    <p>PDFreactor allows JavaScript access to layout information via the proprietary object ro.layout. This layout
        information can mainly be accessed in the form of descriptions of elements inside the document.</p>

    <h2>Descriptions</h2>
    <h3>BoxDescription</h3>
    <p>The Box description describes the position and dimensions of the rectangles of a box as well as information
        about the page the element is on and the text it contains. When getting the box description of an element,
        the "getBoxDecriptions(element)" method will return an array of box descriptions because an element that is
        split over several pages, regions or columns will have several box descriptions.</p>

    <div class="code"><code>let element = document.querySelector("#myElem"); 
let boxDescriptions = ro.layout.getBoxDescriptions(element); 
if (boxDescriptions.length &gt; 0) { 
    let boxDescription = boxDescriptions[0]; 
}</code></div>

    <p>There are 4 different rectangles that can be accessed from the box description. These are the margin
        rectangle, padding rectangle, border rectangle and the content rectangle. These rectangles contain the
        position and dimensions of these specific areas of a box. The unit of these dimensions can be specified
        when getting the rectangles. The point of origin of these rectangles is the upper left corner of the page
        content rectangle but you can also get these rectangles with the point of origin being the upper left corner
        of the whole page.</p>

    <div class="code"><code>let element = document.querySelector("#myElem"); 
let boxDescriptions = ro.layout.getBoxDescriptions(element); 
if (boxDescriptions.length &gt; 0) {
    let boxDescription = boxDescriptions[0]; 
    let marginRect = boxDescription.getMarginRect("cm"); 
    let paddingRect = boxDescription.getPaddingRect("in"); 
    let borderRect = boxDescription.getBorderRect("pt");
    let contentRect = boxDescription.getContentRect("px");

    let marginRectInPage = boxDescription.getMarginRectInPage("cm"); 
    let paddingRectInPage = boxDescription.getPaddingRectInPage("in"); 
    let borderRectInPage = boxDescription.getBorderRectInPage("pt"); 
    let contentRectInPage = boxDescription.getContentRectInPage("px"); 
}</code></div>

    <h3>Page Description</h3>
    <p>The page description describes the dimensions of a page and its rectangles. It can be accessed either by the
        page index or from a box description of an element:</p>

    <div class="code"><code>let pageDesc = ro.layout.getPageDescription(0);

let element = document.querySelector("#myElem"); 
let boxDescriptions = ro.layout.getBoxDescriptions(element); 
if (boxDescriptions.length &gt; 0) { 
    let boxDescription = boxDescriptions[0]; 
    let pageDesc = boxDescription.pageDescription; 
}</code></div>

    <p>The same kind of rectangles that can be accessed from the box description also can be accessed from the page
        description. These rectangles contain information about the dimensions of the page.</p>

    <div class="code"><code>let pageDescription = ro.layout.getPageDescription(0); 
let marginRect = pageDescription.getMarginRect("cm"); 
let paddingRect = pageDescription.getPaddingRect("in"); 
let borderRect = pageDescription.getBorderRect("pt"); 
let contentRect = pageDescription.getContentRect("px");</code></div>

    <p>There are also two kinds of ranges that can be accessed from the page description. The "range" is a DOM range
        where the start- and end container are the most deeply nested nodes at the respective page breaks, while the
        "rangeShallow" is a DOM range where the start and end container are the least deeply nested nodes at the
        respective page breaks. For example if you had a p element inside a div element at a page break, the range would return the p element
        as the end container, while the "rangeShallow" would return the div element as the end container.
    </p>

    <div class="code"><code>let pageDescription = ro.layout.getPageDescription(0); 
let range = pageDescription.range; 
let rangeShallow = pageDescription.rangeShallow;</code></div>

    <h3>Line Description</h3>
    <p>The line description contains information about a line of text. Because an element can contain more than
        one line of text the "lineDescriptions" property of the box description returns an array of line
        descriptions.</p>

    <div class="code"><code>let element = document.querySelector("#myElem"); 
let boxDescriptions = ro.layout.getBoxDescriptions(element); 
if (boxDescriptions.length &gt; 0) { 
    let boxDescription = boxDescriptions[0]; 
    let lineDescriptions = boxDescription.lineDescriptions; 
}</code></div>
    <p>The line description contains information about its content rectangle as well as its baseline position. The
        "getBaselinePosition" method returns the vertical distance between the baseline position of the line and the
        top of the content rectangle of the box containing the line.</p>

    <div class="code"><code>let element = document.querySelector("#myElem"); 
let boxDescriptions = ro.layout.getBoxDescriptions(element); 
if (boxDescriptions.length &gt; 0) { 
    let boxDescription = boxDescriptions[0]; 
    let lineDescriptions = boxDescription.lineDescriptions; 
    if (lineDescriptions.length &gt; 0) {
        let lineDescription = lineDescription[0]; 
        let contentRect = lineDescription.getContentRect("cm"); 
        let baseLinePos = lineDescription.getBaselinePosition("in");
    } 
}</code></div>

    <h2>Use Cases</h2>
    <h3>Page-/Region-/ColumnIndex</h3> 
    
    <p>A frequent use case for the ro.layout object is to find out the page number of a specific element by accessing the page index
        from the box description of the element. However as the page index starts from zero, one has to be added to it to get the page number.
    </p>

    <div class="code"><code>let element = document.querySelector("#myElem"); 
let boxDescriptions = ro.layout.getBoxDescriptions(element); 
if (boxDescriptions.length &gt; 0) { 
    let boxDescription = boxDescriptions[0]; 
    let pageNumber = boxDescription.pageIndex + 1; 
}</code></div>

    <p>You can find out the index of the column/region an element is in by getting the column-/regionIndex from the
        box description. This index starts at 0 for the first column/region and is not reset on new pages or by
        column spans. Alternatively you can also get the column-/regionIndexLocal which is reset on new pages and by
        column spans.</p>

    <div class="code"><code>let element = document.querySelector("#myElem"); 
let boxDescriptions = ro.layout.getBoxDescriptions(element); 
if (boxDescriptions.length &gt; 0) { 
    let boxDescription = boxDescriptions[0];
    let columnIndex = boxDescription.columnIndex; 
    let columnIndexLocal = boxDescription.columnIndexLocal;
}</code></div>

<h3>Styling Elements on Left/Right Pages</h3>
    <p>Elements can be styled, depending on if they are on a right or a left page. This can be achieved by getting the pageLeft attribute
        of the box description of an element.
    </p>
    <div class="code"><code>let element = document.querySelector("#myElem"); 
let boxDescriptions = ro.layout.getBoxDescriptions(element); 
if (boxDescriptions.length &gt; 0) {
    let boxDescription = boxDescriptions[0];
    if (boxDescription.pageLeft) {
        <span class="comment">// Apply styles for elements on a left page.</span>
    } else {
        <span class="comment">// Apply styles for elements on a right page.</span>
    }
}</code></div>

    <h3>Breaks Inside Elements</h3>
    <p>Because elements have several box descriptions if they are split over multiple pages, you can check if an element
        contains a break by checking the length of its box description array.</p>

    <div class="code"><code>let element = document.querySelector("#myElem"); 
let boxDescriptions = ro.layout.getBoxDescriptions(element); 
if (boxDescriptions.length &gt; 0) { 
    let containsBreak = boxDescriptions.length &gt; 1; 
}</code></div>
    <h3>Space Left after an element</h3>
    <p>Another frequent use case is finding out how much space is left after a specific element. This can be done by
        comparing the margin rectangle of the element to the content box of this page. For example a break could
        then be inserted if the remaining space is below a certain threshold.</p>

    <div class="code"><code>let element = document.querySelector("#myElem"); 
let boxDescriptions = ro.layout.getBoxDescriptions(element); 
if (boxDescriptions.length &gt; 0) {
    let boxDescription = boxDescriptions[0]; 
    let pageDescription = boxDescription.pageDescription
    let boxMarginRect = boxDescription.getMarginRect("cm"); 
    let pageContentRect = pageDescription.getContentRect("cm"); 
    let spaceLeft = pageContentRect.bottom - boxMarginRect.bottom; 
}</code></div>

    <p>Please note that the content rectangle of the page is used here rather than the margin rectangle, because the
        margin rectangle of a page includes the page margin boxes.</p>

    <h3>Preventing Text Overflow</h3>
    <p>Another use for the ro.layout object is to find out if text is overflowing. This can be done by getting the
        last line description of a box description and comparing its bottom coordinate to the height of the box description. 
        You then reduce the font-size of this element and repeat this process until the text is not overflowing or a specific 
        font-size minimum is reached.</p>

    <div class="code"><code>let element = document.querySelector("#myElem");
let boxDescriptions = ro.layout.getBoxDescriptions(element);

if (boxDescriptions.length > 0) {
    var height = boxDescriptions[0].getContentRect().height;
    var textOffset = getTextOffset(element);

    <span class="comment">// Get the font size of the element.</span>
    var style = window.getComputedStyle(element, null).getPropertyValue('font-size');
    var fontSize = parseFloat(style);
    console.log(fontSize);

    while (textOffset > height && fontSize >= 10) {
        fontSize -= 0.1;
        element.style.fontSize = fontSize + "px";
        textOffset = getTextOffset(element);
    } 
}

function getTextOffset(paragraph) {
    var lines = ro.layout.getBoxDescriptions(paragraph)[0].lineDescriptions;
    var lastLine = lines[lines.length - 1];

    return lastLine.getContentRect().bottom;
}</code></div>
</body>
</html>