Open File Viewer

repository·main·Indexed 21 days ago

https://github.com/xushanpei/open-file-viewer

A container-first file preview SDK for rendering diverse file formats including PDF, Office, Images, 3D, and CAD within a controlled DOM container. It provides a core engine (@open-file-viewer/core) with official adapters for React, Vue, Svelte, and vanilla JavaScript. The library supports a plugin-based architecture for extending file type support and offers customizable toolbars, locale settings, and high-fidelity Office conversion hooks.

Tokens
34.9K
Snippets
88
Records
119
Agent score
75%

What's inside open-file-viewer

  1. Project roadmap and upcoming features

    main

    The Open File Viewer project follows a structured roadmap for feature expansion:

    • 0.1.x (Current): Core plugin system, in-container preview, framework integrations (React/Vue/Svelte/Vanilla), and basic multi-format preview.
    • 0.2.x: Toolbar, themes, image interactions, PDF search, unified states, and fallback mechanisms.
    • 0.3.x: Markdown/code reader, enhanced Office spreadsheets, and document experience.
    • 0.4.x: Support for OFD, email, archives, drawing files, and domestic business formats.
    • 0.5.x: CAD, 3D, GIS, dedicated parsers, and server-side conversion collaboration.
    • 1.0.0: Stable API, complete documentation site, visual regression tests, and plugin development guide.
  2. Plugin Order and Format Coverage

    main

    Open File Viewer uses a plugin-based architecture where each plugin handles specific file formats.

    Important: Plugin Order Matters Plugins are evaluated in the order they are provided in the plugins array. The first plugin that matches the file format will be used to render it. If a file format matches multiple plugins (e.g., .csv matches both textPlugin() and officePlugin()), you must place the preferred plugin earlier in the array to ensure the correct rendering behavior.

    Supported Categories and Plugins

    • Images: imagePlugin() (jpg, png, gif, webp, etc.)
    • Video: videoPlugin() (mp4, webm, mov, etc.)
    • Audio: audioPlugin() (mp3, wav, ogg, etc.)
    • Text/Code: textPlugin() (txt, md, json, js, ts, etc.)
    • PDF/E-books: pdfPlugin(), epubPlugin(), xpsPlugin()
    • Office: officePlugin() (doc, docx, xls, xlsx, pptx, etc.)
    • Archives: archivePlugin() (zip, rar, 7z, etc.)
    • Email: emailPlugin() (eml, msg, mbox)
    • Drawing/Whiteboard: drawingPlugin() (drawio, excalidraw, etc.)
    • CAD/Engineering: cadPlugin() (dxf, dwg, step, etc.)
    • 3D Models: model3dPlugin() (gltf, glb, obj, etc.)
    • GIS: gisPlugin() (geojson, kml, shp, etc.)
  3. Implement the DWG/DWF two-layer preview model

    main

    The cadPlugin() uses a two-layer design for DWG files:

    1. Default Built-in Capability: Attempts to render line drawings locally using LibreDWG WASM. If WASM is unavailable or fails, it shows metadata and conversion suggestions.
    2. External Enhanced Capability: Use the binaryRenderer hook to take full control of the preview using your own engine (e.g., CADViewer, MxCAD) or a backend-converted asset (PNG/PDF/SVG).

    To use the default LibreDWG line drawing preview, place the WASM files in a public static directory and configure wasmBaseUrl.

    // Enable default LibreDWG line drawing preview
    cadPlugin({
      libreDwg: {
        wasmBaseUrl: "/vendor/libredwg-web"
      }
    });
    
    // Use external engine for high-fidelity rendering
    cadPlugin({
      async binaryRenderer({ panel, extension, arrayBuffer, fileName }) {
        if (extension !== "dwg") return;
    
        const stage = document.createElement("div");
        stage.className = "my-dwg-stage";
        panel.append(stage);
    
        // Load your engine, workers, fonts, etc.
        // await renderDwgWithYourEngine(stage, arrayBuffer, { fileName });
    
        return {
          destroy() {
            stage.remove();
          }
        };
      }
    });
  4. How plugin ordering affects file rendering

    main

    The order of the plugins array is critical because the first plugin that matches the file type will handle the rendering.

    If a file type can be handled by multiple plugins, place the more specific or preferred plugin earlier in the array. For example, csv and tsv files can be handled by both textPlugin() and officePlugin(). If you want a spreadsheet-style table preview instead of plain text, you must place officePlugin() before textPlugin() in your plugins list.

  5. Customize the Toolbar in Svelte

    main

    You can customize the toolbar in two ways:

    1. Slot-based customization: Use the toolbar slot to provide completely custom controls. The slot provides a context object (ctx) containing state and methods like canPrevious, canNext, previous(), next(), download(), and index.

    2. Configuration-based customization: For lighter changes, pass a configuration object to the toolbar prop containing labels, icons, order, and actions.

    <OpenFileViewer files={files} plugins={plugins}>
      <svelte:fragment slot="toolbar" let:ctx>
        {#if ctx}
          <button disabled={!ctx.canPrevious} on:click={() => void ctx.previous()}>上一份</button>
          <span>{ctx.index + 1} / {ctx.length}</span>
          <button disabled={!ctx.canNext} on:click={() => void ctx.next()}>下一份</button>
          <button on:click={ctx.download}>下载</button>
          <button on:click={() => openApprovalDialog(ctx.file)}>审批</button>
        {/if}
      </svelte:fragment>
    </OpenFileViewer>
  6. Markdown rendering capabilities in Open File Viewer

    main

    Open File Viewer supports enhanced Markdown rendering via Marked.js. The renderer dynamically handles various Markdown elements within the browser container, including:

    • Typography & Inline Styles: Italics, bold, inline code, strikethrough, and blockquotes.
    • Lists: Both unordered and ordered lists.
    • Code Blocks: Dynamic syntax highlighting using Prism.js for multiple languages (e.g., TypeScript, Python).
    • Tables: Standard Markdown table structures.
    • Horizontal Rules: Separators using ---.
  7. Plugin order and format matching

    main

    The order of plugins in the plugins array is critical because the first plugin that matches the file format will be the one used to render it.

    Example: A .csv file can be handled by both textPlugin() and officePlugin(). If you want the file to be rendered as a spreadsheet-style table, you must place officePlugin() before textPlugin() in your plugins array.

  8. Lightweight toolbar customization

    main

    For simple adjustments without replacing the entire UI, you can pass configuration options to the toolbar prop. This allows you to modify labels, icons, the order of elements, and available actions without using a slot.

    <!-- Example of the configuration keys available for the toolbar prop -->
    <OpenFileViewer 
      :toolbar="{ 
        labels: { ... }, 
        icons: { ... }, 
        order: [ ... ], 
        actions: [ ... ] 
    }" 
    />
  9. How Open File Viewer works: Container-first and Plugin-based architecture

    main

    Open File Viewer is built on two core architectural principles:

    1. Container-first: All content renders inside a DOM container provided by the user. It does not open new windows or interrupt the host application.
    2. Plugin-based formats: Each file format is handled by an independent plugin. This allows developers to easily replace, remove, or extend specific file-handling behaviors.

    This architecture enables support for a wide range of formats including PDFs, Office documents, images, audio/video, archives, emails, drawings, 3D files, GIS data, and source code.

  10. How the DWG/DWF two-layer preview model works

    main

    The cadPlugin() provides two ways to render DWG/DWF files:

    1. Default built-in path: Uses LibreDWG WASM to render linework. If the WASM path is unavailable or fails, it falls back to metadata and structure probes. To enable this, provide the wasmBaseUrl in the plugin configuration.
    2. External enhancement path: Allows you to provide a custom binaryRenderer. This renderer has the highest priority and can be used to integrate third-party engines (like CADViewer or MxCAD) or backend services that convert files to PNG/PDF/SVG/DXF.

    Use the binaryRenderer option to take full control of the rendering process.

    // Enable default LibreDWG path
    cadPlugin({
      libreDwg: {
        wasmBaseUrl: "/vendor/libredwg-web"
      }
    });
    
    // Use custom external renderer
    cadPlugin({
      async binaryRenderer({ panel, extension, arrayBuffer, fileName }) {
        if (extension !== "dwg") return;
    
        const stage = document.createElement("div");
        stage.className = "my-dwg-stage";
        panel.append(stage);
    
        // Example: await renderDwgWithYourEngine(stage, arrayBuffer, { fileName });
    
        return {
          destroy() {
            stage.remove();
          }
        };
      }
    });
  11. How to develop a custom PreviewPlugin

    main

    A plugin is responsible for matching a file type and rendering it into the provided viewport. A PreviewPlugin must implement match and render methods.

    Plugin Constraints:

    • Render content exclusively inside ctx.viewport.
    • Do not open new windows by default.
    • Implement resize(size) if the plugin needs to react to container size changes.
    • Implement destroy() to clean up events, Object URLs, timers, Canvas/WebGL resources, and other side effects.
    import type { PreviewPlugin } from "@open-file-viewer/core";
    
    export function customPlugin(): PreviewPlugin {
      return {
        name: "custom",
        match(file) {
          return file.extension === "custom";
        },
        async render(ctx) {
          const element = document.createElement("div");
          element.textContent = ctx.file.name;
          ctx.viewport.append(element);
    
          return {
            resize(size) {
              console.log("container resized", size);
            },
            destroy() {
              element.remove();
            }
          };
        }
      };
    }
  12. Develop custom plugins

    main

    A plugin is an object that implements the PreviewPlugin interface. It must define how to match a file and how to render it within the provided viewport.

    Plugin Interface Requirements

    • name: A unique identifier.
    • match(file): A function that returns true if the plugin can handle the file (e.g., by checking file.extension).
    • render(ctx): An asynchronous function that performs the rendering. It must render only inside ctx.viewport.

    Lifecycle and Constraints

    • Viewport Isolation: Always render inside ctx.viewport.
    • Window Management: Do not open new windows by default.
    • Resizing: If your plugin needs to react to container size changes, implement the resize(size) method in the returned object.
    • Cleanup: Implement the destroy() method to release side effects like event listeners, Object URLs, timers, or Canvas/WebGL resources.
    import type { PreviewPlugin } from "@open-file-viewer/core";
    
    export function customPlugin(): PreviewPlugin {
      return {
        name: "custom",
        match(file) {
          return file.extension === "custom";
        },
        async render(ctx) {
          const element = document.createElement("div");
          element.textContent = ctx.file.name;
          ctx.viewport.append(element);
    
          return {
            resize(size) {
              console.log("container resized", size);
            },
            destroy() {
              element.remove();
            }
          };
        }
      };
    }