ohif-viewer/extensions/dicom-pdf/src/DicomPDFViewport.js

263 lines
6.8 KiB
JavaScript
Raw Normal View History

import React, { Component, createRef } from 'react';
2019-04-16 18:00:06 +02:00
import dicomParser from 'dicom-parser';
import PDFJS from 'pdfjs-dist';
import PropTypes from 'prop-types';
2019-04-16 18:00:06 +02:00
import TypedArrayProp from './TypedArrayProp';
import './DicomPDFViewport.css';
import pdfjsBuild from 'pdfjs-dist/build/pdf';
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry';
pdfjsBuild.GlobalWorkerOptions.workerSrc = pdfjsWorker;
2019-04-16 18:00:06 +02:00
// TODO: Should probably use dcmjs for this
const SOP_CLASS_UIDS = {
fix: 🐛 Add DicomLoaderService & FileLoaderService to fix SR, PDF, and SEG support in local file and WADO-RS-only use cases (#862) * fix: 🐛 Local file: failing when retrieving segmentation data Fix segmentation data retrieval issues for local file. Changed from fecthing to use cornerstone loadAndCache method BREAKING CHANGE: DICOM Seg Closes: part of #838 * Switch SEG retrieval to WADO-RS * Forgot a debugger * refactor: 💡 Code refactor. Minor changes into methods * fix: 🐛 Load local files: PDF Items: 1. FileLoaderService: used for serveral operations on local files(load it, get list of studies, group them, accepting dicom and pdf) 2. DicomLoaderService: used for loading dicom based on dataset and studies. Depending on type of dicom loader might change. WIP 3. Refactor PDF and handleSegmentationStorage to use DicomLoaderService * fix: 🐛 Code review * fix: 🐛 Code review. Changed:Folder organization and dicom file Move fileLoaderService and others to a specific folder. When loading dicom file change to only retrieve the file(not use cornerstone to cache or anything else). * fix: 🐛 Code review. Move dicomLoaderService to core Moved dicomLoaderService to ohif/core and localFileLoaders to a specific folder. * fix: 🐛 Code review Simplified method to get study for dicom file. Added error handling on file loading. DicomLoaderService to be exposed on ohif/core/utils instead. * fix: 🐛 Reduce local load to one method only Reduced local file load to one method only * fix: 🐛 HTML to use dicomLoaderService. Prefer wadors than (uri) * fix: 🐛 Code implementation for multiframe files * fix: 🐛 Code review. Default local loader to dicom Closes: 838 * fix: 🐛 Code review. Use relative path to require DICOMWeb Closes: 838 * fix: 🐛 Code review. Fix unit test. Added DicomLoaderService mod Closes: 838 * fix: 🐛 Code review. Add 'Seg' on left thumb When getting/creating dataset get modality for file/image read Closes: 838
2019-09-27 13:47:08 +02:00
ENCAPSULATED_PDF: '1.2.840.10008.5.1.4.1.1.104.1',
2019-04-16 18:00:06 +02:00
};
class DicomPDFViewport extends Component {
constructor(props) {
super(props);
this.state = {
fileURL: null,
error: null,
currentPageIndex: 1,
pdf: null,
scale: 1,
};
this.canvas = createRef();
this.textLayer = createRef();
}
2019-04-16 18:00:06 +02:00
static propTypes = {
fix: 🐛 Add DicomLoaderService & FileLoaderService to fix SR, PDF, and SEG support in local file and WADO-RS-only use cases (#862) * fix: 🐛 Local file: failing when retrieving segmentation data Fix segmentation data retrieval issues for local file. Changed from fecthing to use cornerstone loadAndCache method BREAKING CHANGE: DICOM Seg Closes: part of #838 * Switch SEG retrieval to WADO-RS * Forgot a debugger * refactor: 💡 Code refactor. Minor changes into methods * fix: 🐛 Load local files: PDF Items: 1. FileLoaderService: used for serveral operations on local files(load it, get list of studies, group them, accepting dicom and pdf) 2. DicomLoaderService: used for loading dicom based on dataset and studies. Depending on type of dicom loader might change. WIP 3. Refactor PDF and handleSegmentationStorage to use DicomLoaderService * fix: 🐛 Code review * fix: 🐛 Code review. Changed:Folder organization and dicom file Move fileLoaderService and others to a specific folder. When loading dicom file change to only retrieve the file(not use cornerstone to cache or anything else). * fix: 🐛 Code review. Move dicomLoaderService to core Moved dicomLoaderService to ohif/core and localFileLoaders to a specific folder. * fix: 🐛 Code review Simplified method to get study for dicom file. Added error handling on file loading. DicomLoaderService to be exposed on ohif/core/utils instead. * fix: 🐛 Reduce local load to one method only Reduced local file load to one method only * fix: 🐛 HTML to use dicomLoaderService. Prefer wadors than (uri) * fix: 🐛 Code implementation for multiframe files * fix: 🐛 Code review. Default local loader to dicom Closes: 838 * fix: 🐛 Code review. Use relative path to require DICOMWeb Closes: 838 * fix: 🐛 Code review. Fix unit test. Added DicomLoaderService mod Closes: 838 * fix: 🐛 Code review. Add 'Seg' on left thumb When getting/creating dataset get modality for file/image read Closes: 838
2019-09-27 13:47:08 +02:00
byteArray: TypedArrayProp.uint8,
useNative: PropTypes.bool,
viewportData: PropTypes.object,
activeViewportIndex: PropTypes.number,
setViewportActive: PropTypes.func,
viewportIndex: PropTypes.number,
2019-04-16 18:00:06 +02:00
};
static defaultProps = {
useNative: false,
};
async componentDidMount() {
const dataSet = this.parseByteArray(this.props.byteArray);
const fileURL = this.getPDFFileUrl(dataSet, this.props.byteArray);
this.setState(state => ({ ...state, fileURL }));
if (!this.props.useNative) {
const pdf = await PDFJS.getDocument(fileURL).promise;
this.setState(state => ({ ...state, pdf }), () => this.updatePDFCanvas());
}
}
updatePDFCanvas = async () => {
const { pdf, scale, currentPageIndex } = this.state;
const context = this.canvas.getContext('2d');
const page = await pdf.getPage(currentPageIndex);
let viewport = page.getViewport({ scale });
this.canvas.height = viewport.height;
this.canvas.width = viewport.width;
const renderContext = {
canvasContext: context,
viewport: viewport,
};
await page.render(renderContext);
const textContent = await page.getTextContent();
this.textLayer.innerHTML = '';
this.textLayer.style.height = viewport.height + 'px';
this.textLayer.style.width = viewport.width + 'px';
PDFJS.renderTextLayer({
textContent,
container: this.textLayer,
viewport,
textDivs: [],
});
};
componentDidUpdate(prevProps, prevState) {
const { currentPageIndex, scale } = this.state;
const newValidScale = prevState.scale !== scale && scale > 0;
const newValidPageNumber =
prevState.currentPageIndex !== currentPageIndex && currentPageIndex > 0;
if (newValidScale || newValidPageNumber) {
this.updatePDFCanvas();
}
}
getPDFFileUrl = (dataSet, byteArray) => {
fix: 🐛 Add DicomLoaderService & FileLoaderService to fix SR, PDF, and SEG support in local file and WADO-RS-only use cases (#862) * fix: 🐛 Local file: failing when retrieving segmentation data Fix segmentation data retrieval issues for local file. Changed from fecthing to use cornerstone loadAndCache method BREAKING CHANGE: DICOM Seg Closes: part of #838 * Switch SEG retrieval to WADO-RS * Forgot a debugger * refactor: 💡 Code refactor. Minor changes into methods * fix: 🐛 Load local files: PDF Items: 1. FileLoaderService: used for serveral operations on local files(load it, get list of studies, group them, accepting dicom and pdf) 2. DicomLoaderService: used for loading dicom based on dataset and studies. Depending on type of dicom loader might change. WIP 3. Refactor PDF and handleSegmentationStorage to use DicomLoaderService * fix: 🐛 Code review * fix: 🐛 Code review. Changed:Folder organization and dicom file Move fileLoaderService and others to a specific folder. When loading dicom file change to only retrieve the file(not use cornerstone to cache or anything else). * fix: 🐛 Code review. Move dicomLoaderService to core Moved dicomLoaderService to ohif/core and localFileLoaders to a specific folder. * fix: 🐛 Code review Simplified method to get study for dicom file. Added error handling on file loading. DicomLoaderService to be exposed on ohif/core/utils instead. * fix: 🐛 Reduce local load to one method only Reduced local file load to one method only * fix: 🐛 HTML to use dicomLoaderService. Prefer wadors than (uri) * fix: 🐛 Code implementation for multiframe files * fix: 🐛 Code review. Default local loader to dicom Closes: 838 * fix: 🐛 Code review. Use relative path to require DICOMWeb Closes: 838 * fix: 🐛 Code review. Fix unit test. Added DicomLoaderService mod Closes: 838 * fix: 🐛 Code review. Add 'Seg' on left thumb When getting/creating dataset get modality for file/image read Closes: 838
2019-09-27 13:47:08 +02:00
let pdfByteArray = byteArray;
2019-04-16 18:00:06 +02:00
fix: 🐛 Add DicomLoaderService & FileLoaderService to fix SR, PDF, and SEG support in local file and WADO-RS-only use cases (#862) * fix: 🐛 Local file: failing when retrieving segmentation data Fix segmentation data retrieval issues for local file. Changed from fecthing to use cornerstone loadAndCache method BREAKING CHANGE: DICOM Seg Closes: part of #838 * Switch SEG retrieval to WADO-RS * Forgot a debugger * refactor: 💡 Code refactor. Minor changes into methods * fix: 🐛 Load local files: PDF Items: 1. FileLoaderService: used for serveral operations on local files(load it, get list of studies, group them, accepting dicom and pdf) 2. DicomLoaderService: used for loading dicom based on dataset and studies. Depending on type of dicom loader might change. WIP 3. Refactor PDF and handleSegmentationStorage to use DicomLoaderService * fix: 🐛 Code review * fix: 🐛 Code review. Changed:Folder organization and dicom file Move fileLoaderService and others to a specific folder. When loading dicom file change to only retrieve the file(not use cornerstone to cache or anything else). * fix: 🐛 Code review. Move dicomLoaderService to core Moved dicomLoaderService to ohif/core and localFileLoaders to a specific folder. * fix: 🐛 Code review Simplified method to get study for dicom file. Added error handling on file loading. DicomLoaderService to be exposed on ohif/core/utils instead. * fix: 🐛 Reduce local load to one method only Reduced local file load to one method only * fix: 🐛 HTML to use dicomLoaderService. Prefer wadors than (uri) * fix: 🐛 Code implementation for multiframe files * fix: 🐛 Code review. Default local loader to dicom Closes: 838 * fix: 🐛 Code review. Use relative path to require DICOMWeb Closes: 838 * fix: 🐛 Code review. Fix unit test. Added DicomLoaderService mod Closes: 838 * fix: 🐛 Code review. Add 'Seg' on left thumb When getting/creating dataset get modality for file/image read Closes: 838
2019-09-27 13:47:08 +02:00
if (dataSet) {
Instance metadata/metadata providers overhaul (#1481) * Instance metadata plus metadata provider overhaul. Fix consumption of wado-uri urls fallbacks + datatype agnosticism. WIP DICOMify things. fix various issues with naturalized variable naming migration. Remove metadata provider. Fix consumption of multiframe images and addition of CWIL metadata. Fix strange build issues. Fix CWIL style windowWidth to array from naturalized DICOM. Fix PT, CT, CR and DX issues for cornerstone + DX issues for vtkjs. Move color palette fetching down to the natuaralized JSON level. Remove unused StudyMetadataSummary Remove redundant dicom metadata dictionary. Working local + json routes. Fix SR read. Finished first round of testing + cleaned up debugging etc. * data => metadata for instance naturalizedJSON * Update dcmjs version * Correct github isssues. * Fix erroneously replaced files. * Danny's recommended changes. * Instance metadata plus metadata provider overhaul. Fix consumption of wado-uri urls fallbacks + datatype agnosticism. WIP DICOMify things. fix various issues with naturalized variable naming migration. Remove metadata provider. Fix consumption of multiframe images and addition of CWIL metadata. Fix strange build issues. Fix CWIL style windowWidth to array from naturalized DICOM. Fix PT, CT, CR and DX issues for cornerstone + DX issues for vtkjs. Move color palette fetching down to the natuaralized JSON level. Remove unused StudyMetadataSummary Remove redundant dicom metadata dictionary. Working local + json routes. Fix SR read. Finished first round of testing + cleaned up debugging etc. * data => metadata for instance naturalizedJSON * Update dcmjs version * Correct github isssues. * Fix erroneously replaced files. * Danny's recommended changes. * Update JSON CI * Update casing of import. * Fix jump for SR. * Fix unit tests for measurements service * Fix json CI test. * fix: update yarn lock * Fix local non-encapsulated pdf view * CI updated to new sucess message. Co-authored-by: Danny <danny.ri.brown@gmail.com>
2020-03-09 20:03:23 +01:00
const SOPClassUID = dataSet.string('x00080016');
fix: 🐛 Add DicomLoaderService & FileLoaderService to fix SR, PDF, and SEG support in local file and WADO-RS-only use cases (#862) * fix: 🐛 Local file: failing when retrieving segmentation data Fix segmentation data retrieval issues for local file. Changed from fecthing to use cornerstone loadAndCache method BREAKING CHANGE: DICOM Seg Closes: part of #838 * Switch SEG retrieval to WADO-RS * Forgot a debugger * refactor: 💡 Code refactor. Minor changes into methods * fix: 🐛 Load local files: PDF Items: 1. FileLoaderService: used for serveral operations on local files(load it, get list of studies, group them, accepting dicom and pdf) 2. DicomLoaderService: used for loading dicom based on dataset and studies. Depending on type of dicom loader might change. WIP 3. Refactor PDF and handleSegmentationStorage to use DicomLoaderService * fix: 🐛 Code review * fix: 🐛 Code review. Changed:Folder organization and dicom file Move fileLoaderService and others to a specific folder. When loading dicom file change to only retrieve the file(not use cornerstone to cache or anything else). * fix: 🐛 Code review. Move dicomLoaderService to core Moved dicomLoaderService to ohif/core and localFileLoaders to a specific folder. * fix: 🐛 Code review Simplified method to get study for dicom file. Added error handling on file loading. DicomLoaderService to be exposed on ohif/core/utils instead. * fix: 🐛 Reduce local load to one method only Reduced local file load to one method only * fix: 🐛 HTML to use dicomLoaderService. Prefer wadors than (uri) * fix: 🐛 Code implementation for multiframe files * fix: 🐛 Code review. Default local loader to dicom Closes: 838 * fix: 🐛 Code review. Use relative path to require DICOMWeb Closes: 838 * fix: 🐛 Code review. Fix unit test. Added DicomLoaderService mod Closes: 838 * fix: 🐛 Code review. Add 'Seg' on left thumb When getting/creating dataset get modality for file/image read Closes: 838
2019-09-27 13:47:08 +02:00
Instance metadata/metadata providers overhaul (#1481) * Instance metadata plus metadata provider overhaul. Fix consumption of wado-uri urls fallbacks + datatype agnosticism. WIP DICOMify things. fix various issues with naturalized variable naming migration. Remove metadata provider. Fix consumption of multiframe images and addition of CWIL metadata. Fix strange build issues. Fix CWIL style windowWidth to array from naturalized DICOM. Fix PT, CT, CR and DX issues for cornerstone + DX issues for vtkjs. Move color palette fetching down to the natuaralized JSON level. Remove unused StudyMetadataSummary Remove redundant dicom metadata dictionary. Working local + json routes. Fix SR read. Finished first round of testing + cleaned up debugging etc. * data => metadata for instance naturalizedJSON * Update dcmjs version * Correct github isssues. * Fix erroneously replaced files. * Danny's recommended changes. * Instance metadata plus metadata provider overhaul. Fix consumption of wado-uri urls fallbacks + datatype agnosticism. WIP DICOMify things. fix various issues with naturalized variable naming migration. Remove metadata provider. Fix consumption of multiframe images and addition of CWIL metadata. Fix strange build issues. Fix CWIL style windowWidth to array from naturalized DICOM. Fix PT, CT, CR and DX issues for cornerstone + DX issues for vtkjs. Move color palette fetching down to the natuaralized JSON level. Remove unused StudyMetadataSummary Remove redundant dicom metadata dictionary. Working local + json routes. Fix SR read. Finished first round of testing + cleaned up debugging etc. * data => metadata for instance naturalizedJSON * Update dcmjs version * Correct github isssues. * Fix erroneously replaced files. * Danny's recommended changes. * Update JSON CI * Update casing of import. * Fix jump for SR. * Fix unit tests for measurements service * Fix json CI test. * fix: update yarn lock * Fix local non-encapsulated pdf view * CI updated to new sucess message. Co-authored-by: Danny <danny.ri.brown@gmail.com>
2020-03-09 20:03:23 +01:00
if (SOPClassUID !== SOP_CLASS_UIDS.ENCAPSULATED_PDF) {
fix: 🐛 Add DicomLoaderService & FileLoaderService to fix SR, PDF, and SEG support in local file and WADO-RS-only use cases (#862) * fix: 🐛 Local file: failing when retrieving segmentation data Fix segmentation data retrieval issues for local file. Changed from fecthing to use cornerstone loadAndCache method BREAKING CHANGE: DICOM Seg Closes: part of #838 * Switch SEG retrieval to WADO-RS * Forgot a debugger * refactor: 💡 Code refactor. Minor changes into methods * fix: 🐛 Load local files: PDF Items: 1. FileLoaderService: used for serveral operations on local files(load it, get list of studies, group them, accepting dicom and pdf) 2. DicomLoaderService: used for loading dicom based on dataset and studies. Depending on type of dicom loader might change. WIP 3. Refactor PDF and handleSegmentationStorage to use DicomLoaderService * fix: 🐛 Code review * fix: 🐛 Code review. Changed:Folder organization and dicom file Move fileLoaderService and others to a specific folder. When loading dicom file change to only retrieve the file(not use cornerstone to cache or anything else). * fix: 🐛 Code review. Move dicomLoaderService to core Moved dicomLoaderService to ohif/core and localFileLoaders to a specific folder. * fix: 🐛 Code review Simplified method to get study for dicom file. Added error handling on file loading. DicomLoaderService to be exposed on ohif/core/utils instead. * fix: 🐛 Reduce local load to one method only Reduced local file load to one method only * fix: 🐛 HTML to use dicomLoaderService. Prefer wadors than (uri) * fix: 🐛 Code implementation for multiframe files * fix: 🐛 Code review. Default local loader to dicom Closes: 838 * fix: 🐛 Code review. Use relative path to require DICOMWeb Closes: 838 * fix: 🐛 Code review. Fix unit test. Added DicomLoaderService mod Closes: 838 * fix: 🐛 Code review. Add 'Seg' on left thumb When getting/creating dataset get modality for file/image read Closes: 838
2019-09-27 13:47:08 +02:00
throw new Error('This is not a DICOM-encapsulated PDF');
}
const fileTag = dataSet.elements.x00420011;
const offset = fileTag.dataOffset;
const remainder = offset + fileTag.length;
pdfByteArray = dataSet.byteArray.slice(offset, remainder);
2019-04-16 18:00:06 +02:00
}
const PDF = new Blob([pdfByteArray], { type: 'application/pdf' });
const fileURL = URL.createObjectURL(PDF);
return fileURL;
};
onPageChange = async event => {
const { currentPageIndex, pdf } = this.state;
let newPageIndex = currentPageIndex;
const action = event.target.getAttribute('data-pager');
if (action === 'prev') {
if (currentPageIndex === 1) {
return;
}
newPageIndex -= 1;
if (currentPageIndex < 0) {
newPageIndex = 0;
}
}
if (action === 'next') {
if (currentPageIndex === pdf.numPages - 1) {
return;
}
newPageIndex += 1;
if (currentPageIndex > pdf.numPages - 1) {
newPageIndex = pdf.numPages - 1;
}
}
this.setState(state => ({ ...state, currentPageIndex: newPageIndex }));
};
onZoomChange = () => {
let newZoomValue = this.state.scale;
const action = event.target.getAttribute('data-pager');
if (action === '+') {
newZoomValue += 0.25;
}
if (action === '-') {
newZoomValue -= 0.25;
}
this.setState(state => ({ ...state, scale: newZoomValue }));
2019-04-16 18:00:06 +02:00
};
parseByteArray = byteArray => {
const options = { untilTag: '' };
2019-04-16 18:00:06 +02:00
let dataSet;
try {
dataSet = dicomParser.parseDicom(byteArray, options);
} catch (error) {
this.setState(state => ({ ...state, error }));
2019-04-16 18:00:06 +02:00
}
return dataSet;
};
setViewportActiveHandler = () => {
const {
setViewportActive,
viewportIndex,
activeViewportIndex,
} = this.props;
2019-04-16 18:00:06 +02:00
if (viewportIndex !== activeViewportIndex) {
setViewportActive(viewportIndex);
}
};
downloadPDFCanvas = () => {
const { fileURL } = this.state;
const a = document.createElement('a');
a.href = fileURL;
a.download = fileURL.substr(fileURL.lastIndexOf('/') + 1);
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
2019-04-16 18:00:06 +02:00
render() {
const { fileURL, pdf, error } = this.state;
2019-04-16 18:00:06 +02:00
return (
<div
className={'DicomPDFViewport'}
onClick={this.setViewportActiveHandler}
onScroll={this.setViewportActiveHandler}
2019-04-16 18:00:06 +02:00
style={{ width: '100%', height: '100%' }}
>
{!this.props.useNative ? (
<>
<div id="toolbar">
<div id="pager">
{pdf && pdf.numPages > 1 && (
<>
<button data-pager="prev" onClick={this.onPageChange}>
{`<`}
</button>
<button data-pager="next" onClick={this.onPageChange}>
{`>`}
</button>
</>
)}
<button data-pager="-" onClick={this.onZoomChange}>
{`-`}
</button>
<button data-pager="+" onClick={this.onZoomChange}>
{`+`}
</button>
<button onClick={this.downloadPDFCanvas}>Download</button>
</div>
</div>
<div id="canvas">
<div id="pdf-canvas-container">
<canvas
id="pdf-canvas"
ref={canvas => (this.canvas = canvas)}
/>
<div
id="text-layer"
ref={textLayer => (this.textLayer = textLayer)}
></div>
</div>
</div>
</>
) : (
2019-04-16 18:00:06 +02:00
<object
aria-label="PDF Viewer"
data={fileURL}
2019-04-16 18:00:06 +02:00
type="application/pdf"
width="100%"
height="100%"
/>
)}
{error && <h2>{JSON.stringify(error)}</h2>}
2019-04-16 18:00:06 +02:00
</div>
);
}
}
export default DicomPDFViewport;