0

I'm building an Angular app where I intend to let the user draw text fields over PDF pages. For this, I'm using ng2-pdf-viewer library and I have a PDF page component that will hold a PDF page, as well as its corresponding HTML canvas element.

The problem I'm facing is that the canvas turns black when I start drawing on the respective PDF page. Although I am able to draw successfully, this means the whole page background turns to black.

Here is how it looks (the PDF page with content is behind that black background):

enter image description here

My page component code is the following:

export class PdfPreviewPageComponent {
    @Input() pdfSrc: string = '';
    @Input() pdfPageNumber!: number;

    @ViewChild('pdfViewer', { static: false, read: ElementRef }) pdfViewerRef!: ElementRef;

    private canvas!: HTMLCanvasElement;
    private cx: CanvasRenderingContext2D | null = null;
    private isDrawing: boolean = false;
    private startX!: number;
    private startY!: number;

    // UPDATE: changed from getting the canvas via input and using ngOnChanges to
    // using the (page-rendered) event provided by ng2-pdf-viewer library
    onPageLoaded(event: Event) {
        if (!this.canvas) {
            this.canvas = this._document.getElementsByTagName('canvas')[this.pdfPageNumber - 1];
            this.ctx = this.canvas.getContext('2d');

            if (this.ctx) {
                this.ctx.fillStyle = 'red';
                this.ctx.lineWidth = 2;
                this.handleCanvasEvent();
            }
        }
    }

    private handleCanvasEvent() {
        this.canvas.addEventListener('mousemove', (event) => this.onMouseMove(event));
        this.canvas.addEventListener('mousedown', (event) => this.onMouseDown(event));
        this.canvas.addEventListener('mouseup', (event) => this.onMouseUp(event));
    }

    private onMouseDown(event: MouseEvent) {
        this.isDrawing = true;
        this.startX = event.clientX - this.canvas.getBoundingClientRect().left;
        this.startY = event.clientY - this.canvas.getBoundingClientRect().top;
    }

    private onMouseMove(event: MouseEvent) {
        if (!this.isDrawing) {
            return;
        }

        const currentX = event.clientX - this.canvas.getBoundingClientRect().left;
        const currentY = event.clientY - this.canvas.getBoundingClientRect().top;
        const width = currentX - this.startX;
        const height = currentY - this.startY;

        this.clearCanvas();
        this.drawRectangle(this.startX, this.startY, width, height);
    }

    private onMouseUp(event: MouseEvent) {
        this.isDrawing = false;
    }

    private clearCanvas() {
        if (this.cx) {
            this.cx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        }
    }

    private drawRectangle(x: number, y: number, width: number, height: number) {
        if (this.cx) {
            this.cx.strokeRect(x, y, width, height);
        }
    }
    ...
    ...
}

How can I avoid the canvas turning completely black? I don't want to hide the content of the PDF page as the whole purpose is for the user to draw text fields (rectangles) where they may see fit.

Here is the stackblitz project.

Manuel Brás
  • 413
  • 7
  • 21
  • Hello @Manuel, my first code impression tells me to ask a question: why you are getting the 2D context in the ` ngOnChanges(changes: SimpleChanges) { ... }` ? I'm not sure but I don't guess it's the right place to do so. I would suggest to get it in the `ngAfterViewInit() {..}`, and then reference it by a local component class property.. – sohaieb azaiez May 25 '23 at 17:53
  • @sohaiebazaiez I get what you mean. The problem is that canvas will be undefined if I set the 2D context in the AfterViewInit hook. Even if I try to bypass this using a Subject and waiting for the emission in the AfterViewInit hook, I still get the same black background. I suspect this background problem might be due to the canvas clear method. Whenever I remove it, the background does not change which is what I want. However, since I only want to use the last iteration of the drawn rectangle, removing the previous iterations with the clear method is necessary. – Manuel Brás May 25 '23 at 20:16
  • to be honest the black screen can happen in many different cases, that's why we should really investigate it step by step.. Otherwise, if `ngAfterViewInit() {..}` returns undefined, this means there is something wrong in the setup of the component in relation with the Canvas Html Element itself... So for that, I have another question: did you try to change `static: false` to `true`, exemple: ` @ViewChild('pdfViewer', { static: false, ... })` to ` @ViewChild('pdfViewer', { static: true, ... })` and check the 2D context again? – sohaieb azaiez May 25 '23 at 20:31
  • @sohaiebazaiez I tried that and the result was the same unfortunately. – Manuel Brás May 29 '23 at 09:32
  • well I'm sorry to hear that.. but as I mentioned previously, the black screen can happen in many cases different cases.. so one thing left: if you can reproduce your partial code in [StackBlitz (SB)](https://stackblitz.com/) so we can reproduce & investigate it directly on SB? maybe this would help all people to intervern too ^^ – sohaieb azaiez May 29 '23 at 09:40
  • @sohaiebazaiez Sure thing! Here you go [link](https://stackblitz.com/edit/stackblitz-starters-kvaseu?file=src%2Fapp%2Fpdf-preview%2Fpdf-preview.component.ts) – Manuel Brás May 29 '23 at 10:31
  • Hello again, thank you for the link, this helps a lot.. I guess the problem was that the file you are testing on, has something wrong. I tested with another file and this worked for me, you can check [this link](https://stackblitz.com/edit/stackblitz-starters-bormxf?file=src%2Fapp%2Fpdf-preview%2Fpdf-preview.component.ts) and tell me if this solves your black issue problem? – sohaieb azaiez May 29 '23 at 11:07
  • @sohaiebazaiez In the stackblitz you provided I still get the black canvas issue, this is weird. Are you sure that when you try to draw the rectangle there is no black canvas? – Manuel Brás May 29 '23 at 11:16
  • Yes you are totally right, I'm sorry it was my bad that I wrote a comment then I deleted it but I see it still here yet lol .. but I'm already working on the investigation, I hope I can help you as soon as possible ^^ – sohaieb azaiez May 29 '23 at 11:19
  • I updated the code: moved away from ngOnChanges and started using the (page-rendered) event from provided by ng2-pdf-viewer library. It cleans up the code a bit but I stil get the black screen. I updated the Stackblitz also. – Manuel Brás Jun 15 '23 at 10:02

1 Answers1

0

The problem is that I was trying to use and manipulate the canvas that the ng2-pdf-viewer library uses on each of its PDF viewer elements. This is the canvas that renders the PDF content, so once you call any canvas clearing methods (such as clearRect()) on this canvas, you will inevitably delete all of the PDF page content.

The correct approach is to create your own canvas elements and overlay them on top of their respective pages, using absolute or relative positioning and making sure each canvas is on top of the stacking order with z-index.

Manuel Brás
  • 413
  • 7
  • 21