Categories
Java Script Tutorials

Combine Two PDF Pages into One with variable width

This post is the extension to the previous tutorial. This tutorial add the width and height controls. User can change the width of embedded page and can visually see it’s impact on final PDF in realtime.

Implementation

We will continue with updating the HTML markup

<main>
  <input id="fileInput" type="file" accept=".pdf" />
  <label>Width <input class="widthInput" step="25" type="number" min="200" max="600" /></label>
  <label>Height <input readonly class="heightInput" type="number" min="0" max="500" /></label>
</main>

We have added the two inputs. One for width and one for height. The height one is readonly as we want to maintain the aspect ratio when width is changed, more on that later.

.widthInput, .heightInput {
  width: 5rem;
}

We just increased the default width of those input elements.

// output goes here

const widthEl = document.querySelector('input[type="number"].widthInput') as HTMLInputElement
const heightEl = document.querySelector('input[type="number"].heightInput') as HTMLInputElement

widthEl?.addEventListener('change',  event => {
  const value = (event.target as HTMLInputElement).valueAsNumber
  createDocument(firstPage, secondPage, value)
}

We have some references of dom elements with some type assertions to HTMLInputElements to silence some typescript errors.

Next we listen to change in the width input and react to the document creation accordingly. Our createDocument function accepts third argument, guess what the width

What’s new in create document?

async function createDocument(pageAToEmbed: PDFPage, pageBToEmbed: PDFPage, width = PageSizes.A4[0] ) {
  // .....
  let [, height] = PageSizes.A4;
  const calcHeight = aspectCorrectDimension(width);

The third argument is width= PageSize.A4[0], we are setting the default value to A4 page width for the first run of the function. As this function also runs when the width is changed.

In Line 3 we skip the width and only de-structure the height

Let’s explain the what’s happening on Line 4

aspectCorrectDimension

We need to calculate the aspect ratio of the A4 page. The dimension of A4 page is 545 x 841 in pixels. We embedded takes half of the page so our dimensions becomes 545 x 420.5. The aspect ratio becomes

545/420.5=1.296545 / 420.5 =1.296
/**
 * Decimal Ratio
 * 545 ÷ 420.5 ≈ 1.296
 */
function aspectCorrectDimension(width: number) {
  const aR = 1.296;
  return width / aR
}

Our function calculates the appropriate height to maintain the aspect ratio.

  page.drawPage(embeddedPageA, { x: 10, y: height/2, width: width, height: calcHeight })

  widthEl.value = String(width); 
  heightEl.value = String(calcHeight);

  page.drawPage(embeddedPageB, { x: 10, y: 0, width, height: calcHeight})
  .../
}

We have update the height config for the drawPage method with newly calculatedHeight. We also need to update the input controls as well and that’s it.

Output

Horizontally centering

const remainder = (PageSizes.A4[0] - width) / 2
const centeredX = remainder

page.drawPage(embeddedPageA, { x: centeredX, y: height/2, width: width, height: calcHeight })

//...

page.drawPage(embeddedPageB, { x: centeredX, y: 0, width, height: calcHeight})

We have updated the x: centeredX which is calculated by some subtraction and division. This way, our faces remains always in the center.

Conclusion

We have introduced some input elements. Change in width recalculates the dimension of card face while maintaining the aspect ration and embeds it.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.