Skip to content

Documentation Request: Using [customPdfViewer] #2853

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
anaceurenssup opened this issue Mar 30, 2025 · 9 comments
Open

Documentation Request: Using [customPdfViewer] #2853

anaceurenssup opened this issue Mar 30, 2025 · 9 comments
Assignees
Labels
documentation user support You've got a question, or a misconfiguration, or simply need a hint how to get things up and running

Comments

@anaceurenssup
Copy link

anaceurenssup commented Mar 30, 2025

Documentation Request: Using [customPdfViewer] and Resolving pdf-dummy-components Import

I'm trying to add an interactive layer (grid/list of buttons) on top of a PDF using customPdfViewer input but I'm struggling to make the default PDF viewer (ngx-extended-pdf-viewer.component.html: <ng-template #defaultPdfViewer> ...) template work as custom one (so i can add my layer)!

<ngx-extended-pdf-viewer #pdfViewer [src]="'pdf.pdf'"
  [customPdfViewer]="customViewer"></ngx-extended-pdf-viewer>
  
<ng-template #customViewer>
  <div class="zoom" #root>
    <div class="html">
      <div class="body">
        <div id="outerContainer">
          <div id="mainContainer">
            <pdf-dummy-components></pdf-dummy-components>
            <div class="editorParamsToolbar hidden doorHangerRight" id="editorHighlightParamsToolbar">
              <div id="highlightParamsToolbarContainer" class="editorParamsToolbarContainer">
                <div id="editorHighlightColorPicker" class="colorPicker">
                  <span id="highlightColorPickerLabel" class="editorParamsLabel"
                    data-l10n-id="pdfjs-editor-highlight-colorpicker-label">Highlight color</span>
                </div>
                <div id="editorHighlightThickness">
                  <label for="editorFreeHighlightThickness" class="editorParamsLabel"
                    data-l10n-id="pdfjs-editor-free-highlight-thickness-input">Thickness</label>
                  <div class="thicknessPicker">
                    <input type="range" id="editorFreeHighlightThickness" class="editorParamsSlider"
                      data-l10n-id="pdfjs-editor-free-highlight-thickness-title" value="12" min="8" max="24" step="1" />
                  </div>
                </div>
                <div id="editorHighlightVisibility">
                  <div class="divider"></div>
                  <div class="toggler">
                    <label for="editorHighlightShowAll" class="editorParamsLabel"
                      data-l10n-id="pdfjs-editor-highlight-show-all-button-label">Show all</label>
                    <button id="editorHighlightShowAll" class="toggle-button"
                      data-l10n-id="pdfjs-editor-highlight-show-all-button" aria-pressed="true"></button>
                  </div>
                </div>
              </div>
            </div>

            <div class="editorParamsToolbar hidden doorHangerRight" id="editorFreeTextParamsToolbar">
              <div class="editorParamsToolbarContainer">
                <div class="editorParamsSetter">
                  <label for="editorFreeTextColor" class="editorParamsLabel"
                    data-l10n-id="pdfjs-editor-free-text-color-input">Font Color</label>
                  <input type="color" id="editorFreeTextColor" class="editorParamsColor" />
                </div>
                <div class="editorParamsSetter">
                  <label for="editorFreeTextFontSize" class="editorParamsLabel"
                    data-l10n-id="pdfjs-editor-free-text-size-input">Font Size</label>
                  <input type="range" id="editorFreeTextFontSize" class="editorParamsSlider" value="10" min="5"
                    max="100" step="1" />
                </div>
              </div>
            </div>
    
            <div class="editorParamsToolbar hidden doorHangerRight" id="editorStampParamsToolbar">
              <div class="editorParamsToolbarContainer">
                <button id="editorStampAddImage" class="secondaryToolbarButton" title="Add image"
                  data-l10n-id="pdfjs-editor-stamp-add-image-button">
                  <svg aria-hidden="true" focusable="false" width="16" height="16" viewBox="0 0 16 16" fill="none"
                    xmlns="http://www.w3.org/2000/svg" class="align-image-to-text">
                    <path
                      d="M7.00488 9.75V14C7.00488 14.1658 7.07073 14.3247 7.18794 14.4419C7.30515 14.5592 7.46412 14.625 7.62988 14.625C7.79564 14.625 7.95461 14.5592 8.07183 14.4419C8.18904 14.3247 8.25488 14.1658 8.25488 14V9.75L8.75488 9.25H13.0049C13.1706 9.25 13.3296 9.18415 13.4468 9.06694C13.564 8.94973 13.6299 8.79076 13.6299 8.625C13.6299 8.45924 13.564 8.30027 13.4468 8.18306C13.3296 8.06585 13.1706 8 13.0049 8H8.75488L8.25488 7.5V3.25C8.25488 3.08424 8.18904 2.92527 8.07183 2.80806C7.95461 2.69085 7.79564 2.625 7.62988 2.625C7.46412 2.625 7.30515 2.69085 7.18794 2.80806C7.07073 2.92527 7.00488 3.08424 7.00488 3.25V7.5L6.50488 8H2.25488C2.08912 8 1.93015 8.06585 1.81294 8.18306C1.69573 8.30027 1.62988 8.45924 1.62988 8.625C1.62988 8.79076 1.69573 8.94973 1.81294 9.06694C1.93015 9.18415 2.08912 9.25 2.25488 9.25H6.39188L7.00488 9.75Z"
                      fill="black" />
                  </svg>
                  <span data-l10n-id="pdfjs-editor-stamp-add-image-button-label">Add image</span>
                </button>
              </div>
            </div>
    
            <div class="editorParamsToolbar hidden doorHangerRight" id="editorInkParamsToolbar">
              <div class="editorParamsToolbarContainer">
                <div class="editorParamsSetter">
                  <label for="editorInkColor" class="editorParamsLabel"
                    data-l10n-id="pdfjs-editor-ink-color-input">Color</label>
                  <input type="color" id="editorInkColor" class="editorParamsColor" />
                </div>
                <div class="editorParamsSetter">
                  <label for="editorInkThickness" class="editorParamsLabel"
                    data-l10n-id="pdfjs-editor-ink-thickness-input">Thickness</label>
                  <input type="range" id="editorInkThickness" class="editorParamsSlider" value="1" min="1" max="20"
                    step="1" />
                </div>
                <div class="editorParamsSetter">
                  <label for="editorInkOpacity" class="editorParamsLabel"
                    data-l10n-id="pdfjs-editor-ink-opacity-input">Opacity</label>
                  <input type="range" id="editorInkOpacity" class="editorParamsSlider" value="1" min="0.05" max="1"
                    step="0.05" />
                </div>
              </div>
            </div>




            <pdf-context-menu></pdf-context-menu>
    
            <div id="viewerContainer">
    
              <div id="viewer" class="pdfViewer"></div>
            </div>
            <pdf-error-message></pdf-error-message>
          </div>
          <!-- mainContainer -->
    
          <div id="dialogContainer">
            <pdf-password-dialog></pdf-password-dialog>
            <pdf-document-properties-dialog></pdf-document-properties-dialog>
            <pdf-alt-text-dialog></pdf-alt-text-dialog>
            <pdf-alt-text-settings-dialog></pdf-alt-text-settings-dialog>
            <pdf-prepare-printing-dialog></pdf-prepare-printing-dialog>
          </div>
          <!-- dialogContainer -->
        </div>
    
        <!-- outerContainer -->
      </div>
    </div>
  </div>

</ng-template>

(when trying to use , the component was not exported (created a custom module that manually declares/imports pdf-dummy-components, but uncertain if this is the correct approach)

(i just removed all of the properties/parts ('minHeight', <pdf-sidebar #pdfsidebar [sidebarVisible]="sidebarVisible || false", ...) to figure why it does not want to work / I don't know best approach to have this properties and parts by default like using inheritance from ngx-extended-pdf-viewer.component)

Could you please provide documentation/examples for the proper usage of [customPdfViewer] property? or a quick fix or any recommended way to implement interactive overlays)

Thank you

@stephanrauh stephanrauh self-assigned this Mar 31, 2025
@stephanrauh stephanrauh added documentation user support You've got a question, or a misconfiguration, or simply need a hint how to get things up and running labels Mar 31, 2025
@stephanrauh
Copy link
Owner

You're right, there's no demo yet, and that's a shame!

However, judging from my source code, I believe the feature works. So let's go one step back. If I get you right, you're trying to replace the default PDF viewer HTML by your custom HTML, but it refuses to do so?

BTW, maybe it's simpler and more future-proof to put your buttons on top of the PDF viewer, using CSS magic to place the buttons at the correct position. But that's just guesswork, I don't know anything about your application.

@anaceurenssup
Copy link
Author

Thanks for the suggestion! That’s a great approach for single-page viewer mode; in fact, I’ve already implemented something similar in another framework-free app.

But for this case, I’m using the PDF viewer in scrolling mode, where each page has its own independent grid. If I overlay the entire viewer with a single grid using CSS, it won’t sync properly when scrolling; the grid would need to dynamically stick to each page and update as the user navigates. That feels more fragile than just replacing the default HTML per page.

Still, I appreciate the idea! It’s a solid solution for the right context

I hope the code works correctly & I was just missing the correct way to implement it, if so, putting an example here would help

@stephanrauh
Copy link
Owner

Oh, yes. You're right, I should have seen this on first read. Well, in that case, [customPdfViewer] doesn't help you because it renders everything, with the exception of the PDF document. That's rendered outside Angular.

What you can do is capture the (pageRendered) event. It notifies you which <canvas> and which <div> belong to the page that's just been rendered. Probably the <div> is more useful, so have a look at event.source.div. The page number is also part of the event.

Now can add your own overlay <div> to that page and draw your buttons on the overlay. That way, the buttons are rendered lazily, and they scroll with the page.

If the user is zooming or rotating the page, the page is rendered again. I don't know if the old <div> and <canvas> are reused or thrown away - there's some research left for you. You should also make sure to discard the buttons on ngDestroy(). When scrolling through a large document, pages are evicted from the cache, and I think this also means the page <div> is replaced by a dummy <div>. Unfortunately, cache eviction doesn't emit an event yet. If that's an issue to you, please tell me.

A more pressing issue is that all that happens outside Angular. If you want to have an Angular button - more precisely: if you want to modify attributes when clicking the button - you must notify Angular programmatically that something has changed. As far as I remember, you have two options: ngZone.run() and triggering the change detector manually. My custom thumbnails demo uses the latter approach, so that's probably the easier way (see https://pdfviewer.net/extended-pdf-viewer/custom-thumbnails).

@anaceurenssup
Copy link
Author

anaceurenssup commented Apr 3, 2025

Sorry for the delayed reply. Regarding (pageRendered), it sounds like a good approach. However, I’m not quite sure why [customPdfViewer] wouldn’t work for that, the overlay isn’t directly attached to the HTML structure of the PDF itself but rather to the page. So while it’s "attached," it’s not a true attachment in that sense hh. This is why I prefer to stay within Angular’s scope instead of diving deep into the DOM, avoiding potential issues with handling clicks as you mentioned in your response.

In my case, hovering or clicking on the grid should affect its styles, but zooming or rotating the page shouldn’t impact the original div.

(I’m considering rendering the PDF content using HTML instead. I was just looking for the shortest path to achieving a V1.)

Here’s the previous framework-free of the app: https://the-ten-readings.github.io (from the bottom bar select page 1 (the page -3 does not have any layer) & hover over the image)

@stephanrauh
Copy link
Owner

Whow, beautiful!

@stephanrauh
Copy link
Owner

I suggested adding the grid overlay to the PDF pages because you mentioned scrolling. Single-page mode makes everything much easier. But if you want to support scrolling, you need to scroll you overlay with the page. And that's easier if your overlay is part of the page - you have to struggle with the Angular change detection, but scrolling becomes easy.

@anaceurenssup
Copy link
Author

Thanks. You're right. I apologize. I thought using the customPdfViewer input would get the job done, but I wasn't fully focused.

So, the reason for writing this issue no longer exists, but I think it will remain open as a reminder to complete the documentation.

Thanks again.

@stephanrauh
Copy link
Owner

stephanrauh commented Apr 3, 2025

Oh, come on, there's no reason to apologize. Your train of thought was totally valid, and if you're using single-page mode, I'm pretty much convinced it will work. Plus, you made me aware of a gap in my documentation, and - last not least - you showed me a stunningly beautiful rendering of the Quran. It's me who has to say "thank you"!

@stephanrauh
Copy link
Owner

Also see #2829 (comment). Over there, a developer implemented my recommended approach.

As mentioned above, your original idea is probably also valid in your particular use-case, I just wanted to add a pointer that might be useful to future readers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation user support You've got a question, or a misconfiguration, or simply need a hint how to get things up and running
Projects
None yet
Development

No branches or pull requests

2 participants