PDF Generation Template Editor
Open-source visual PDF generation engine with customizable templates and developer-friendly APIs.
Open-source visual PDF generation engine with customizable templates and developer-friendly APIs.
The ComPDFKit engine uses a custom EventBus for cross-module communication. The UI layer listens to engine events to keep its state in sync and exposes addEvent / removeEvent so integrators can react to the same events.
The engine's EventBus is exposed through the core bridge:
instance.Core.eventBus(); // the engine's event bus
instance.Core.addEvent(name, handler);
instance.Core.removeEvent(name, handler);eventBus.js in the bridge is a thin accessor — it returns core.getDocumentViewer().eventBus, exposing the engine's own bus. It holds no state of its own.
| Event | Fires when |
|---|---|
annotationSelected / annotationDeselected | An annotation is selected / deselected. |
annotationChanged | Annotations are added/modified/deleted. |
toolChanged | The active tool changes. |
toolModeChanged | The tool mode changes. |
scalechanging | The zoom scale changes. |
pagerendered | A page finishes rendering. |
documentloaded | The document finishes loading. |
onPagesUpdated | Pages are updated (syncs outlines, page count, current page, scale, scroll/spread mode). |
openAnnotationReply | An annotation reply thread is opened. |
updateAnnotationPermission | Annotation permissions change. |
contentEditHistoryChanged / historyChanged | Content-edit / general history changes (drives undo/redo flags). |
changeRightPanelBtnDisabled | Right-panel button disabled state changes. |
presentationmodechanged | Presentation mode toggles. |
Components register listeners in their setup/mount lifecycle. For example, DocumentContainer.vue registers updateAnnotationPermission, toolModeChanged, toolChanged, annotationSelected/annotationDeselected, openAnnotationReply, onPagesUpdated, annotationChanged, and documentloaded during initViewer().
The createHeaderItemsListener (in components/HeaderItems/HeaderItemsListener.js) watches activeTool and selectedAnnotation to recompute the four property-panel button disabled flags and subscribes to annotationSelected, annotationDeselected, changeRightPanelBtnDisabled, and presentationmodechanged.
useHistoryListener bridges contentEditHistoryChanged / historyChanged into useDocument.setCanUndo / setCanRedo.
Lifecycle note:
helpers/hooks/useOnAnnotationPopupOpen.jsregistersannotationSelected/annotationDeselectedand removes them viaonScopeDispose, so listeners are cleaned up when the owning scope (component setup oreffectScope) disposes. When registering listeners yourself throughcore.addEvent, mirror this by removing them inonUnmounted(oronScopeDispose) to avoid leaks.
instance.Core.addEvent('documentloaded', () => {
console.log('Document loaded');
});const handler = (annotations, type) => {
console.log('annotations', type, annotations);
};
instance.Core.addEvent('annotationChanged', handler);
// later
instance.Core.removeEvent('annotationChanged', handler);instance.Core.addEvent('toolChanged', (toolName) => {
console.log('Active tool:', toolName);
});Beyond engine events, the iframe↔parent handshake uses postMessage:
{ type: 'viewerLoaded', docId } to window.parent when the UI is ready (App/index.vue onMounted).webviewer.js listens for it, captures iframe.contentWindow.instance, fires a ready CustomEvent on the host element, and posts { type: 'viewerLoaded', options } back into the iframe.Integrators can listen for the ready event on the host element, or simply await ComPDFKitViewer.init(...) which resolves with instance once ready.
eventBus, addEvent, removeEvent.useOnAnnotationPopupOpen.