OG
JS
Docs
Demos
Examples
Image export lab
Download the full viewport, crop a report to selected entities, or add a classification watermark.
SVG
PNG
PDF
Selection PNG
2× report PNG
Watermarked PDF
Download generated file ↓
// Choose full, selected, or watermarked export intent against the live graph. // OGJS owns viewport scaling, crop bounds, vector/raster composition, and file encoding; // applications do not read the WebGL canvas or reconstruct screen coordinates. import { Ogjs, prismPack } from '../../src/index'; const og = new Ogjs({ container: '#viz', theme: 'light' }); const exportColors = prismPack.context.export; const categories = prismPack.categorical; og.setGraph({ nodes: [ { id: 'center', x: 0, y: 0, size: 18, color: exportColors.node, label: 'Case' }, { id: 'account', x: -180, y: 105, size: 11, color: categories[1].light, label: 'Account' }, { id: 'device', x: 180, y: 105, size: 11, color: categories[2].light, label: 'Device' }, { id: 'merchant', x: 180, y: -105, size: 11, color: categories[6].light, label: 'Merchant' }, { id: 'location', x: -180, y: -105, size: 11, color: categories[7].light, label: 'Location' }, ], edges: [ { source: 'center', target: 'account', color: exportColors.edge }, { source: 'center', target: 'device', color: exportColors.edge }, { source: 'center', target: 'merchant', color: exportColors.edge }, { source: 'center', target: 'location', color: exportColors.edge }, ] }); // Retained framing keeps this intent through resize without application observers or frame loops. og.camera.fit({ target: 'all', avoid: document.querySelector('.workflow-panel'), paddingPx: 24, maxOccupancy: .52, retain: true, }); const download = document.querySelector('#image-download'); const status = document.querySelector('#image-export-status'); let objectUrl; async function generate(format, startDownload = true) { // Each branch supplies export intent—vector, full raster, selected crop, or watermark. OGJS // owns bounds, layer composition, scaling, and encoding for the requested artifact. let blob, filename; if (format === 'svg') { blob = new Blob([og.export.svg()], { type: 'image/svg+xml' }); filename = 'network-view.svg'; } else if (format === 'png') { blob = await og.export.png(); filename = 'network-view.png'; } else if (format === 'selection-png') { og.clearSelection(); for (const id of ['center', 'account', 'device']) og.setSelected(id, true); blob = await og.export.png({ crop: 'selection', padding: 28 }); filename = 'selected-entities.png'; } else if (format === 'report-png') { // Report output is twice the current pixel size, uses a declared paper background, and // excludes geographic/analytic underlays while keeping graph and overlay annotations. blob = await og.export.png({ scale: 2, background: exportColors.canvas, layers: ['graph', 'overlay'], }); filename = 'network-report@2x.png'; } else if (format === 'watermarked-pdf') { blob = await og.export.pdf({ watermark: { text: 'INTERNAL REVIEW', position: 'bottom-right', opacity: 0.68 }, }); filename = 'network-review.pdf'; } else { blob = await og.export.pdf(); filename = 'network-view.pdf'; } // Delivery remains application-owned. Revoke the old URL before replacing the download target // so completed exports do not accumulate browser-held blobs. if (objectUrl) URL.revokeObjectURL(objectUrl); objectUrl = URL.createObjectURL(blob); download.href = objectUrl; download.download = filename; download.classList.add('ready'); status.textContent = `${format.replace('-', ' ').toUpperCase()} · ${blob.size} bytes · ${ 'width' in blob ? `${blob.width} × ${blob.height} px · ` : '' }${ format === 'selection-png' ? '3 selected entities' : 'current viewport' }`; document.querySelectorAll('[data-image-export]').forEach((button) => button.setAttribute('aria-pressed', String(button.dataset.imageExport === format))); if (startDownload) download.click(); } document.querySelectorAll('[data-image-export]').forEach((button) => button.addEventListener('click', () => generate(button.dataset.imageExport))); generate('svg', false);
<!doctype html> <html lang="en"><head><meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1"/> <title>Ogjs example — Image export lab</title> <link rel="stylesheet" href="../site.css"/><link rel="stylesheet" href="./data-lifecycle.css"/> <style>@media (max-width:760px){.workflow-panel button{min-height:44px}}</style> </head><body> <header class="rail"><a class="wordmark" href="../index.html">OG<b>JS</b></a><nav> <a href="../docs.html">Docs</a><a href="../demos.html">Demos</a> <a href="./index.html" aria-current="page">Examples</a></nav></header> <div id="viz" role="region" aria-label="Export preview"></div> <main class="workflow-panel"><h1>Image export lab</h1> <p>Download the full viewport, crop a report to selected entities, or add a classification watermark.</p> <div class="controls"> <button data-image-export="svg">SVG</button> <button data-image-export="png">PNG</button> <button data-image-export="pdf">PDF</button> <button data-image-export="selection-png">Selection PNG</button> <button data-image-export="report-png">2× report PNG</button> <button data-image-export="watermarked-pdf">Watermarked PDF</button> </div> <div class="status" id="image-export-status" aria-live="polite"></div> <a class="download" id="image-download">Download generated file ↓</a> </main> <script type="module"> // Choose full, selected, or watermarked export intent against the live graph. // OGJS owns viewport scaling, crop bounds, vector/raster composition, and file encoding; // applications do not read the WebGL canvas or reconstruct screen coordinates. import { Ogjs, prismPack } from '../../src/index'; const og = new Ogjs({ container: '#viz', theme: 'light' }); const exportColors = prismPack.context.export; const categories = prismPack.categorical; og.setGraph({ nodes: [ { id: 'center', x: 0, y: 0, size: 18, color: exportColors.node, label: 'Case' }, { id: 'account', x: -180, y: 105, size: 11, color: categories[1].light, label: 'Account' }, { id: 'device', x: 180, y: 105, size: 11, color: categories[2].light, label: 'Device' }, { id: 'merchant', x: 180, y: -105, size: 11, color: categories[6].light, label: 'Merchant' }, { id: 'location', x: -180, y: -105, size: 11, color: categories[7].light, label: 'Location' }, ], edges: [ { source: 'center', target: 'account', color: exportColors.edge }, { source: 'center', target: 'device', color: exportColors.edge }, { source: 'center', target: 'merchant', color: exportColors.edge }, { source: 'center', target: 'location', color: exportColors.edge }, ] }); // Retained framing keeps this intent through resize without application observers or frame loops. og.camera.fit({ target: 'all', avoid: document.querySelector('.workflow-panel'), paddingPx: 24, maxOccupancy: .52, retain: true, }); const download = document.querySelector('#image-download'); const status = document.querySelector('#image-export-status'); let objectUrl; async function generate(format, startDownload = true) { // Each branch supplies export intent—vector, full raster, selected crop, or watermark. OGJS // owns bounds, layer composition, scaling, and encoding for the requested artifact. let blob, filename; if (format === 'svg') { blob = new Blob([og.export.svg()], { type: 'image/svg+xml' }); filename = 'network-view.svg'; } else if (format === 'png') { blob = await og.export.png(); filename = 'network-view.png'; } else if (format === 'selection-png') { og.clearSelection(); for (const id of ['center', 'account', 'device']) og.setSelected(id, true); blob = await og.export.png({ crop: 'selection', padding: 28 }); filename = 'selected-entities.png'; } else if (format === 'report-png') { // Report output is twice the current pixel size, uses a declared paper background, and // excludes geographic/analytic underlays while keeping graph and overlay annotations. blob = await og.export.png({ scale: 2, background: exportColors.canvas, layers: ['graph', 'overlay'], }); filename = 'network-report@2x.png'; } else if (format === 'watermarked-pdf') { blob = await og.export.pdf({ watermark: { text: 'INTERNAL REVIEW', position: 'bottom-right', opacity: 0.68 }, }); filename = 'network-review.pdf'; } else { blob = await og.export.pdf(); filename = 'network-view.pdf'; } // Delivery remains application-owned. Revoke the old URL before replacing the download target // so completed exports do not accumulate browser-held blobs. if (objectUrl) URL.revokeObjectURL(objectUrl); objectUrl = URL.createObjectURL(blob); download.href = objectUrl; download.download = filename; download.classList.add('ready'); status.textContent = `${format.replace('-', ' ').toUpperCase()} · ${blob.size} bytes · ${ 'width' in blob ? `${blob.width} × ${blob.height} px · ` : '' }${ format === 'selection-png' ? '3 selected entities' : 'current viewport' }`; document.querySelectorAll('[data-image-export]').forEach((button) => button.setAttribute('aria-pressed', String(button.dataset.imageExport === format))); if (startDownload) download.click(); } document.querySelectorAll('[data-image-export]').forEach((button) => button.addEventListener('click', () => generate(button.dataset.imageExport))); generate('svg', false); </script></body></html>