DEV Community

Atsushi
Atsushi

Posted on

Introduction of PDF Viewer for GROWI Plug-in

GROWI, an open source wiki, has a plug-in feature. You can use it to display your own data or customize the display.

This time, we will introduce a PDF viewer I created as a GROWI plugin, which can be easily extended by creating it as a Remark plugin.

FireShot Capture 358 - PDF - GROWI - localhost 358.jpg

About the code

The code is goofmint/growi-plugin-pdf-viewer: Viewing PDF File inside of GROWI
The code can be found at The two files to view are as follows.

Add the plugin

To use it, please add it at Plugin in the admin panel of GROWI, the URL is https://github.com/goofmint/growi-plugin-pdf-viewer.

FireShot Capture 359 - Plugin - GROWI - localhost 359.jpg

Usage

The plugin uses the notation $pdfviewer. Upload a PDF file in the editor and write $pdfviewer(filename) to display the PDF viewer.

For example, the following statement.

$pdfviewer(/attachment/669c803453e575a188286be3)
Enter fullscreen mode Exit fullscreen mode

However, the embedding size will not work as it is, so you need to adjust the width and height options.

$pdfviewer(/attachment/669c803453e575a188286be3,width=75%,height=850px)
Enter fullscreen mode Exit fullscreen mode

About implementation

Here is the code for the registration part of the plugin client-entry.tsx.

import { plugin } from './src/pdfviewer';

const activate = (): void => {
  if (growiFacade == null || growiFacade.markdownRenderer == null) {
    return;
  }

  const { optionsGenerators } = growiFacade.markdownRenderer;

  optionsGenerators.customGenerateViewOptions = (...args) => {
    const options = optionsGenerators.generateViewOptions(...args);
        // プラグインを追加
    options.remarkPlugins.push(plugin as any);
    return options;
  };
};
Enter fullscreen mode Exit fullscreen mode

PDF Viewer part

This time, I use ts-pdf based on PDF.js as the PDF Viewer; It needs a web worker, so I get it from Cloudfare CDN. It also needs a div tag to display the PDF so it renders it once and then (500ms later) displays the PDF.

Several times this plugin code part has been called, so we are checking if there is a Shadow DOM in the rendered div tag. If there is, we do not initialize the PDF viewer.

The div tag id is hashed with the file path using sha256. This allows multiple PDF viewers within a single page.

import sha256 from 'crypto-js/sha256';
import { TsPdfViewer, TsPdfViewerOptions } from 'ts-pdf';
import type { Plugin } from 'unified';
import { visit } from 'unist-util-visit';

interface GrowiNode extends Node {
  name: string;
  type: string;
  attributes: {[key: string]: string}
  children: GrowiNode[];
  value: string;
}

export const plugin: Plugin = function() {
  return (tree) => {
    visit(tree, (node) => {
      const n = node as unknown as GrowiNode;
      try {
        if (n.type === 'leafGrowiPluginDirective' && n.name === 'pdfviewer') {
          const filePath = Object.keys(n.attributes)[0];
          const { width, height } = n.attributes;
          // ファイル名からユニークなIDを生成
          const key = sha256(filePath);
          const containerSelector = `#pdf-${key}`;
          const options: TsPdfViewerOptions = {
            containerSelector,
            workerSource: 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.worker.min.js',
          };
          n.type = 'html';
          // PDFビューワーを表示するdivタグをレンダリング
          n.value = `<div id="pdf-${key}" style="width: ${width}; height: ${height};"></div>`;
          // HTML描画を待って、500ms後にPDFビューワーを初期化
          setTimeout(() => {
            if (!document.querySelector(containerSelector)?.shadowRoot) {
              const viewer = new TsPdfViewer(options);
              viewer.openPdfAsync(filePath);
            }
          }, 500);
        }
      }
      catch (e) {
        n.type = 'html';
        n.value = `<div style="color: red;">Error: ${(e as Error).message}</div>`;
      }
    });
  };
};
Enter fullscreen mode Exit fullscreen mode

Notes.

PDF.js has a function to adjust the display size, but ts-pdf does not, so the display size must be adjusted manually. Also, I can't find an option to specify a CMap file, and currently it does not support the display of Japanese PDFs (if you know how to specify it, please let me know!).

Summary

GROWI's Remark plugin allows you to freely extend the Markdown notation. If there are any missing features, you can add more and more. By all means, customize your own wiki.

GROWI, an OSS development wiki tool | comfortable information sharing for all

Top comments (0)