Skip to content Skip to sidebar Skip to footer

How To Split Html Page In A4 Size In Angular 9

I am trying to create something like in Xing the CV maker -> https://lebenslauf.com/. I do have different Arrays of Objects. But I am not able to create the A4 pages which will

Solution 1:

It is all about dividing the given content to fit into the give page size.

We can create a component that will handle the dividing functionality for us. here is a StackBlitz Demo.

And here is a brief explanation.

Use the ContentChildren decorator to observe the change in content. every time the content changes we will run the page creation logic.

import {
  AfterContentInit,
  AfterViewInit,
  Component,
  ContentChildren,
  ElementRef,
  Input,
  OnInit,
  QueryList,
  ViewChild
} from "@angular/core";

@Component({
  selector: "app-paginated-view",
  templateUrl: "paginated-view.component.html",
  styleUrls: ["paginated-view.component.scss"]
})
export classPaginatedViewComponentimplementsAfterViewInit{
  @Input() pageSize: "A3" | "A4" = "A4";

  @ViewChild("paginatedView") paginatedView: ElementRef<HTMLDivElement>;

  @ViewChild("contentWrapper") contentWrapper: ElementRef<HTMLDivElement>;

  @ContentChildren("pageContent", { read: ElementRef }) elements: QueryList<
    ElementRef
  >;

  constructor() {}

  ngAfterViewInit(): void {
    this.updatePages();

    // when ever childs updated call the updatePagesfunctionthis.elements.changes.subscribe(el => {
      this.updatePages();
    });
  }

  updatePages(): void {
    // clear paginated viewthis.paginatedView.nativeElement.innerHTML = "";

    // get a new page and add it to the paginated view
    let page = this.getNewPage();
    this.paginatedView.nativeElement.appendChild(page);

    let lastEl: HTMLElement;
    // add content childrens to the page one by onethis.elements.forEach(elRef => {
      const el = elRef.nativeElement;

      // if the content child height is larger than the size of the page// then do not add it to the pageif (el.clientHeight > page.clientHeight) {
        return;
      }
      // add the child to the page
      page.appendChild(el);

      // after adding the child if the page scroll hight becomes larger than the page height// then get a new page and append the child to the  new pageif (page.scrollHeight > page.clientHeight) {
        page = this.getNewPage();
        this.paginatedView.nativeElement.appendChild(page);
        page.appendChild(el);
      }
      lastEl = el;
    });

    //bring the element in to view port
    lastEl.scrollIntoView({ behavior: "smooth", block: "nearest" });
  }

  getNewPage(): HTMLDivElement {
    const page = document.createElement("div");
    page.classList.add("page");
    page.classList.add(this.pageSize);
    return page;
  }
}

We can use this component in an application like this.

<app-paginated-view [pageSize]="'A4'"><h1 #pageContent>Hello World!!</h1><p #pageContent>This content will be displayed in an A4 size page</p></app-paginated-view>

We have to provide the template variable #pageContent so that we can select them using @ContentChildren in our PaginatedViewComponent.

Note that we are using native dom APIs here to change the dom structure. it will only move the dom node from one place to another so if you have any event listener added or have any property binding to the content children they will work as it is.

Edit: I have also updated your stackblitz https://stackblitz.com/edit/angular-ivy-zjf8rv

Solution 2:

enter image description here In app.component.html file:

<div class="page" *ngFor="let page of pages; index as i"
  [style.height]="sizePage.height + 'cm'"
  [style.width]="sizePage.width + 'cm'"
  (click)="clickPage(i)">
  <div class="content" 
    [style.paddingTop]="paddingPage.top + 'cm'"
    [style.paddingRight]="paddingPage.right + 'cm'"
    [style.paddingBottom]="paddingPage.bottom + 'cm'"
    [style.paddingLeft]="paddingPage.left + 'cm'"  
    [id]="'content-' + i" contenteditable="true"
    (input)="inputContent($event['data'], i)">
  </div>
</div>

In app.component.css file:

.page {
    background: white;
    display: block;
    margin: 40px auto;
    box-shadow: 000.5cmrgba(0, 0, 0, 0.5);
    box-sizing: border-box;
}
.page.content {
    overflow: auto;
    outline: 0;
}

In app.component.ts file:

import { AfterViewChecked, Component } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export classAppComponentimplementsAfterViewChecked{
  
  sizePage = {
    width: 21, //cm
    height: 29.7//cm
  }
  paddingPage = {
    top: 2, //cm
    right: 2, //cm
    bottom: 2, //cm
    left: 2//cm
  }
  pages = [
    {
      htmlContent: null,
      full: false
    },
  ]
  currentPage = 0;
  currentChar = null;

  runAfterViewChecked = false;
  
  clickPage(i) {
    this.currentPage = i;
  }

  inputContent(char, i) {
    var element = document.getElementById('content-' + i)
    var heightContent = element.offsetHeight * 2.54 / 96; // Convert pixels to cmthis.pages[i].htmlContent = element.innerHTML;
    console.log(this.pages);
    if (Number(heightContent.toFixed(1)) > this.sizePage.height) { 
      this.currentChar = char;
      this.pages[i].full = true;
      if (!this.pages[i + 1]) {
        this.pages.push({
          htmlContent: null,
          full: false
        })
      }
      this.currentPage = i + 1;
      this.runAfterViewChecked = true;
    }
  }

  ngAfterViewChecked() {
    document.getElementById('content-' + this.currentPage).focus();
    if (this.runAfterViewChecked) {
      if (this.currentChar) {
        var str = this.pages[this.currentPage-1].htmlContent;
        var indexLastCloseDiv = str.lastIndexOf("</div>");
        var indexLastBr = str.lastIndexOf("<br>");
        var lastChar = str[indexLastCloseDiv-1];
        if (indexLastBr != -1 && (indexLastBr + 4) == indexLastCloseDiv)
          lastChar = ' ';

        if (indexLastCloseDiv != -1)
          str = str.slice(0, indexLastCloseDiv-1) + str.slice(indexLastCloseDiv);
        else 
          str = str.slice(0, str.length - 1);
        this.pages[this.currentPage-1].htmlContent = str;

        if (this.pages[this.currentPage].htmlContent)
          this.pages[this.currentPage].htmlContent = lastChar + this.pages[this.currentPage].htmlContent;
        elsethis.pages[this.currentPage].htmlContent = lastChar;
      }

      var element = null;
      for (let i = 0; i < this.pages.length; i++) {
        element = document.getElementById('content-' + i);
        element.innerHTML = this.pages[i].htmlContent;
      }
      this.runAfterViewChecked = false;
    }
  }
}

Link to Stackblitz

Here is a simple example. There are a few mistakes, please give your suggestions for further development.

Some functions such as Backspace, Delete, Scale Page, ... have not been processed.

Solution 3:

If you want to creat html A4 like office word A4 you must use these size:

body{
 width: 21cm ;
 height: 29.7cm;
 margin:30mm45mm30mm45mm;}

Solution 4:

It would be fairly easy to force the web browser to display the page with the same pixel dimensions as A4. However, there may be a few quirks when things are rendered.

Assuming your monitors display 72 dpi, you could add something like this:

<!DOCTYPE html><html><head><style>body {
        height: 842px;
        width: 595px;
        /* to centre page on screen*/margin-left: auto;
        margin-right: auto;
    }
    </style></head><body></body></html>

Here an example in your code with printable A4 size: https://stackblitz.com/edit/angular-ivy-fjhpdu?embed=1&file=src/app/app.component.html

Solution 5:

Actually I cant see any js or css by you that attempts to solve the issue.

But looking at the provided example from xing, they are using static pixel width/height

width: 595px;
height: 842px;

this matches a PPI (pixel per inch) of 72 for the given Format A4 thats the only important thing to take in mind.

Knowing that, you can just check if that height is exceeded while editing and manipulate the DOM accordingly ("create new Page", "split/move elements or parts", etc. ...) You should have everything now to solve yourself. But expect that there is a lot of time going into this, especially regarding keeping font sizes / rendering/printring matching and not to talk about the mobile version ;)

Good luck

Post a Comment for "How To Split Html Page In A4 Size In Angular 9"