// PDF Viewer instance
let viewer;
// Overlay container
let overlayLayer = null;
// Stores created overlays by annotation id
const overlays = new Map();
window.onload = function () {
viewer = new DsPdfViewer("#viewer");
viewer.addDefaultPanels();
viewer.addAnnotationEditorPanel();
viewer.addFormEditorPanel();
viewer.onAfterOpen.register(async () => {
createOverlayLayer();
updateLayerTransform();
const pages = await viewer.annotations;
render3DAnnotations(pages);
// Update positions on scroll
viewer.scrollView.addEventListener("scroll", updateAllOverlayPositions);
// Update positions on zoom
viewer.eventBus.on("scalechange", () => {
updateLayerTransform();
// Delay slightly so zoom finishes first
setTimeout(updateAllOverlayPositions, 0);
});
// Update positions on resize
window.addEventListener("resize", () => {
updateLayerTransform();
updateAllOverlayPositions();
});
});
viewer.open("/document-solutions/javascript-pdf-viewer/demos/product-bundles/assets/pdf/custom-annotation-rendering.pdf");
};
/**
* Creates overlay layer once.
*/
function createOverlayLayer() {
if (overlayLayer)
return;
const documentView =
viewer.hostElement.querySelector(".gcv-document-view");
overlayLayer = document.createElement("div");
overlayLayer.className = "overlay-layer";
// Prevent layer itself from blocking mouse events
overlayLayer.style.pointerEvents = "none";
documentView.appendChild(overlayLayer);
}
/**
* Align overlay coordinates with window coordinates.
*/
function updateLayerTransform() {
const documentView =
viewer.hostElement.querySelector(".gcv-document-view");
if (!documentView || !overlayLayer)
return;
const rect = documentView.getBoundingClientRect();
overlayLayer.style.transform =
`translate(${-rect.left}px, ${-rect.top}px)`;
}
/**
* Creates overlays once.
*/
function render3DAnnotations(pages) {
for (const page of pages) {
for (const annotation of page.annotations) {
if (annotation.subtype !== "3D")
continue;
const id =
annotation.id ||
`${page.pageIndex}_${annotation.rect.join("_")}`;
// Skip if already created
if (overlays.has(id))
continue;
const overlay = create3DOverlay();
overlays.set(id, {
overlay,
pageIndex: page.pageIndex,
annotation
});
overlayLayer.appendChild(overlay);
}
}
updateAllOverlayPositions();
}
/**
* Updates positions of all overlays.
*/
function updateAllOverlayPositions() {
for (const item of overlays.values()) {
const rect =
viewer.convertPdfRectToWindowCoordinates(
item.pageIndex,
item.annotation.rect
);
updateOverlayPosition(item.overlay, rect);
}
}
/**
* Creates a single overlay.
*/
function create3DOverlay() {
const overlay = document.createElement("div");
overlay.className = "model3d-placeholder";
overlay.style.position = "absolute";
// Allow interaction with iframe
overlay.style.pointerEvents = "auto";
overlay.innerHTML = `
<iframe
src="https://viewer.ctech.com/inline_viewer.html?apikey=CEBA68C1CC918347&model=https%3A%2F%2Fviewer.ctech.com%2Fmodels%2Fcoastal-facility.ctws"
width="100%"
height="100%"
frameborder="0"
allowfullscreen
></iframe>
`;
return overlay;
}
/**
* Updates overlay position and size.
*/
function updateOverlayPosition(overlay, rect) {
const [x1, y1, x2, y2] = rect;
overlay.style.left = `${x1}px`;
overlay.style.top = `${y1}px`;
overlay.style.width = `${x2 - x1}px`;
overlay.style.height = `${y2 - y1}px`;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Custom Annotation Rendering with External Interactive Content</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="./src/styles.css">
<script src="/document-solutions/javascript-pdf-viewer/demos/product-bundles/build/dspdfviewer.js"></script>
<script src="/document-solutions/javascript-pdf-viewer/demos/product-bundles/build/wasmSupportApi.js"></script>
<script src="/document-solutions/javascript-pdf-viewer/demos/resource/js/init.js"></script>
<script src="./src/app.js"></script>
</head>
<body>
<div id="viewer"></div>
</body>
</html>
#viewer {
height: 100%;
}
.overlay-layer {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 1000;
}
.model3d-placeholder {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.75);
color: white;
font-size: 24px;
font-weight: bold;
border: 2px dashed white;
box-sizing: border-box;
}