chore: Clean up unused files, imports, and work on getting end-to-end tests and unit tests running (#2272)
This commit is contained in:
1 parent
414ebc6850
commit
0db81b30f3
374 files changed
+14138
-21288
No files matched your search
@@ -1,27 +0,0 @@
|
||||
import OHIF from '@ohif/core';
|
||||
import { connect } from 'react-redux';
|
||||
import OHIFDicomPDFViewport from './OHIFDicomPDFViewport';
|
||||
|
||||
const { setViewportActive } = OHIF.redux.actions;
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const { activeViewportIndex } = state.viewports;
|
||||
return { activeViewportIndex };
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch, ownProps) => {
|
||||
const { viewportIndex } = ownProps;
|
||||
|
||||
return {
|
||||
setViewportActive: () => {
|
||||
dispatch(setViewportActive(viewportIndex));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedOHIFDicomPDFViewer = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(OHIFDicomPDFViewport);
|
||||
|
||||
export default ConnectedOHIFDicomPDFViewer;
|
||||
@@ -1,62 +0,0 @@
|
||||
.DicomPDFViewport {
|
||||
--header-height: 50px;
|
||||
}
|
||||
|
||||
.DicomPDFViewport #toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
padding: 0.5em;
|
||||
border-bottom: var(--ui-border-thickness) solid var(--ui-border-color);
|
||||
height: var(--header-height);
|
||||
}
|
||||
|
||||
.DicomPDFViewport #canvas {
|
||||
height: calc(100% - var(--header-height));
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.DicomPDFViewport #text-layer {
|
||||
height: 100%;
|
||||
margin-top: var(--header-height);
|
||||
}
|
||||
|
||||
.DicomPDFViewport #pdf-canvas-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.DicomPDFViewport canvas, #text-layer {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.DicomPDFViewport #toolbar button {
|
||||
color: currentColor;
|
||||
background-color: transparent;
|
||||
font: inherit;
|
||||
border: var(--ui-border-thickness) solid var(--ui-border-color);
|
||||
border-radius: 3px;
|
||||
padding: 0.25em 0.5em;
|
||||
margin-right: 0.5em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.DicomPDFViewport #text-layer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
opacity: 0.2;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.DicomPDFViewport #text-layer>span {
|
||||
color: transparent;
|
||||
position: absolute;
|
||||
white-space: pre;
|
||||
cursor: text;
|
||||
transform-origin: 0% 0%;
|
||||
}
|
||||
@@ -1,265 +0,0 @@
|
||||
import React, { Component, createRef } from 'react';
|
||||
import dicomParser from 'dicom-parser';
|
||||
import PDFJS from 'pdfjs-dist';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
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;
|
||||
|
||||
// TODO: Should probably use dcmjs for this
|
||||
const SOP_CLASS_UIDS = {
|
||||
ENCAPSULATED_PDF: '1.2.840.10008.5.1.4.1.1.104.1',
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
byteArray: TypedArrayProp.uint8,
|
||||
useNative: PropTypes.bool,
|
||||
viewportData: PropTypes.object,
|
||||
activeViewportIndex: PropTypes.number,
|
||||
setViewportActive: PropTypes.func,
|
||||
viewportIndex: PropTypes.number,
|
||||
};
|
||||
|
||||
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) => {
|
||||
let pdfByteArray = byteArray;
|
||||
|
||||
if (dataSet) {
|
||||
const SOPClassUID = dataSet.string('x00080016');
|
||||
|
||||
if (SOPClassUID !== SOP_CLASS_UIDS.ENCAPSULATED_PDF) {
|
||||
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);
|
||||
}
|
||||
|
||||
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 }));
|
||||
};
|
||||
|
||||
parseByteArray = byteArray => {
|
||||
const options = { untilTag: '' };
|
||||
|
||||
let dataSet;
|
||||
try {
|
||||
dataSet = dicomParser.parseDicom(byteArray, options);
|
||||
} catch (error) {
|
||||
this.setState(state => ({ ...state, error }));
|
||||
}
|
||||
|
||||
return dataSet;
|
||||
};
|
||||
|
||||
setViewportActiveHandler = () => {
|
||||
const {
|
||||
setViewportActive,
|
||||
viewportIndex,
|
||||
activeViewportIndex,
|
||||
} = this.props;
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { fileURL, pdf, error } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={'DicomPDFViewport'}
|
||||
onClick={this.setViewportActiveHandler}
|
||||
onScroll={this.setViewportActiveHandler}
|
||||
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>
|
||||
</>
|
||||
) : (
|
||||
<object
|
||||
aria-label="PDF Viewer"
|
||||
data={fileURL}
|
||||
type="application/pdf"
|
||||
width="100%"
|
||||
height="100%"
|
||||
/>
|
||||
)}
|
||||
{error && <h2>{JSON.stringify(error)}</h2>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DicomPDFViewport;
|
||||
@@ -1,15 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
function OHIFComponentPlugin(props) {
|
||||
return (<React.Fragment>{props.children}</React.Fragment>);
|
||||
}
|
||||
|
||||
OHIFComponentPlugin.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
init: PropTypes.func.isRequired,
|
||||
destroy: PropTypes.func.isRequired,
|
||||
children: PropTypes.node
|
||||
};
|
||||
|
||||
export default OHIFComponentPlugin;
|
||||
@@ -1,29 +0,0 @@
|
||||
import { MODULE_TYPES, utils } from '@ohif/core';
|
||||
|
||||
// TODO: Should probably use dcmjs for this
|
||||
const SOP_CLASS_UIDS = {
|
||||
ENCAPSULATED_PDF: '1.2.840.10008.5.1.4.1.1.104.1',
|
||||
};
|
||||
|
||||
const OHIFDicomPDFSopClassHandler = {
|
||||
id: 'OHIFDicomPDFSopClassHandlerPlugin',
|
||||
type: MODULE_TYPES.SOP_CLASS_HANDLER,
|
||||
sopClassUIDs: [SOP_CLASS_UIDS.ENCAPSULATED_PDF],
|
||||
getDisplaySetFromSeries(series, study, dicomWebClient, authorizationHeaders) {
|
||||
const instance = series.getFirstInstance();
|
||||
|
||||
return {
|
||||
plugin: 'pdf',
|
||||
Modality: 'DOC',
|
||||
displaySetInstanceUID: utils.guid(),
|
||||
wadoRoot: study.getData().wadoRoot,
|
||||
wadoUri: instance.getData().wadouri,
|
||||
SOPInstanceUID: instance.getSOPInstanceUID(),
|
||||
SeriesInstanceUID: series.getSeriesInstanceUID(),
|
||||
StudyInstanceUID: study.getStudyInstanceUID(),
|
||||
authorizationHeaders: authorizationHeaders,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export default OHIFDicomPDFSopClassHandler;
|
||||
@@ -1,72 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import OHIF from '@ohif/core';
|
||||
import OHIFComponentPlugin from './OHIFComponentPlugin.js';
|
||||
import DicomPDFViewport from './DicomPDFViewport';
|
||||
|
||||
const { DicomLoaderService } = OHIF.utils;
|
||||
|
||||
class OHIFDicomPDFViewport extends Component {
|
||||
static propTypes = {
|
||||
studies: PropTypes.object,
|
||||
displaySet: PropTypes.object,
|
||||
viewportIndex: PropTypes.number,
|
||||
viewportData: PropTypes.object,
|
||||
activeViewportIndex: PropTypes.number,
|
||||
setViewportActive: PropTypes.func,
|
||||
};
|
||||
|
||||
state = {
|
||||
byteArray: null,
|
||||
error: null,
|
||||
};
|
||||
|
||||
static id = 'DicomPDFViewportPDF';
|
||||
|
||||
static init() {
|
||||
console.log('DicomPDFViewport init()');
|
||||
}
|
||||
|
||||
static destroy() {
|
||||
console.log('DicomPDFViewport destroy()');
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { displaySet, studies } = this.props.viewportData;
|
||||
|
||||
DicomLoaderService.findDicomDataPromise(displaySet, studies).then(
|
||||
data => this.setState({ byteArray: new Uint8Array(data) }),
|
||||
error => {
|
||||
this.setState({ error });
|
||||
throw new Error(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
setViewportActive,
|
||||
viewportIndex,
|
||||
activeViewportIndex,
|
||||
} = this.props;
|
||||
const { byteArray, error } = this.state;
|
||||
const { id, init, destroy } = OHIFDicomPDFViewport;
|
||||
const pluginProps = { id, init, destroy };
|
||||
|
||||
return (
|
||||
<OHIFComponentPlugin {...pluginProps}>
|
||||
{byteArray && (
|
||||
<DicomPDFViewport
|
||||
byteArray={byteArray}
|
||||
setViewportActive={setViewportActive}
|
||||
viewportIndex={viewportIndex}
|
||||
activeViewportIndex={activeViewportIndex}
|
||||
/>
|
||||
)}
|
||||
{error && <h2>{JSON.stringify(error)}</h2>}
|
||||
</OHIFComponentPlugin>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default OHIFDicomPDFViewport;
|
||||
@@ -1,137 +0,0 @@
|
||||
// https://github.com/facebook/prop-types/issues/69
|
||||
|
||||
export const TypedArrayProp = {
|
||||
any: (props, propName, componentName) => {
|
||||
let obj = props[propName];
|
||||
if (!(obj instanceof Float64Array ||
|
||||
obj instanceof Float32Array ||
|
||||
obj instanceof Int32Array ||
|
||||
obj instanceof Int16Array ||
|
||||
obj instanceof Int8Array ||
|
||||
obj instanceof Uint32Array ||
|
||||
obj instanceof Uint16Array ||
|
||||
obj instanceof Uint8Array ||
|
||||
obj instanceof Uint8ClampedArray)) {
|
||||
return new Error(
|
||||
'Invalid prop `' + propName + '` supplied to' +
|
||||
' `' + componentName + '`. Expected a typed array.'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
float64: (props, propName, componentName) => {
|
||||
if (!(props[propName] instanceof Float64Array)) {
|
||||
return new Error(
|
||||
'Invalid prop `' + propName + '` supplied to' +
|
||||
' `' + componentName + '`. Expected a Float64Array.'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
float32: (props, propName, componentName) => {
|
||||
if (!(props[propName] instanceof Float32Array)) {
|
||||
return new Error(
|
||||
'Invalid prop `' + propName + '` supplied to' +
|
||||
' `' + componentName + '`. Expected a Float32Array.'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
float: (props, propName, componentName) => {
|
||||
if (!(props[propName] instanceof Float64Array ||
|
||||
props[propName] instanceof Float32Array)) {
|
||||
return new Error(
|
||||
'Invalid prop `' + propName + '` supplied to' +
|
||||
' `' + componentName + '`. Expected a Float32Array or Float64Array.'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
int32: (props, propName, componentName) => {
|
||||
if (!(props[propName] instanceof Int32Array)) {
|
||||
return new Error(
|
||||
'Invalid prop `' + propName + '` supplied to' +
|
||||
' `' + componentName + '`. Expected an Int32Array.'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
int16: (props, propName, componentName) => {
|
||||
if (!(props[propName] instanceof Int16Array)) {
|
||||
return new Error(
|
||||
'Invalid prop `' + propName + '` supplied to' +
|
||||
' `' + componentName + '`. Expected an In16Array.'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
int8: (props, propName, componentName) => {
|
||||
if (!(props[propName] instanceof Int8Array)) {
|
||||
return new Error(
|
||||
'Invalid prop `' + propName + '` supplied to' +
|
||||
' `' + componentName + '`. Expected an Int8Array.'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
int: (props, propName, componentName) => {
|
||||
if (!(props[propName] instanceof Int32Array ||
|
||||
props[propName] instanceof Int16Array ||
|
||||
props[propName] instanceof Int8Array)) {
|
||||
return new Error(
|
||||
'Invalid prop `' + propName + '` supplied to' +
|
||||
' `' + componentName + '`. Expected an Int32Array, In16Array, or Int8Array.'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
uint32: (props, propName, componentName) => {
|
||||
if (!(props[propName] instanceof Uint32Array)) {
|
||||
return new Error(
|
||||
'Invalid prop `' + propName + '` supplied to' +
|
||||
' `' + componentName + '`. Expected a Uint32Array.'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
uint16: (props, propName, componentName) => {
|
||||
if (!(props[propName] instanceof Uint16Array)) {
|
||||
return new Error(
|
||||
'Invalid prop `' + propName + '` supplied to' +
|
||||
' `' + componentName + '`. Expected a Uint16Array.'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
uint8: (props, propName, componentName) => {
|
||||
if (!(props[propName] instanceof Uint8Array)) {
|
||||
return new Error(
|
||||
'Invalid prop `' + propName + '` supplied to' +
|
||||
' `' + componentName + '`. Expected a Uint8Array.'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
uint8clamped: (props, propName, componentName) => {
|
||||
if (!(props[propName] instanceof Uint8ClampedArray)) {
|
||||
return new Error(
|
||||
'Invalid prop `' + propName + '` supplied to' +
|
||||
' `' + componentName + '`. Expected a Uint8ClampedArray.'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
uint: (props, propName, componentName) => {
|
||||
if (!(props[propName] instanceof Uint32Array ||
|
||||
props[propName] instanceof Uint16Array ||
|
||||
props[propName] instanceof Uint8Array ||
|
||||
props[propName] instanceof Uint8ClampedArray)) {
|
||||
return new Error(
|
||||
'Invalid prop `' + propName + '` supplied to' +
|
||||
' `' + componentName + '`. Expected a Uint32Array, Uint16Array, Uint8Array, or Uint8ClampedArray.'
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export { TypedArrayProp as default };
|
||||
@@ -1,27 +0,0 @@
|
||||
import React from 'react';
|
||||
import OHIFDicomPDFSopClassHandler from './OHIFDicomPDFSopClassHandler.js';
|
||||
|
||||
const Component = React.lazy(() => {
|
||||
return import('./ConnectedOHIFDicomPDFViewer');
|
||||
});
|
||||
|
||||
const ConnectedOHIFDicomPDFViewer = props => {
|
||||
return (
|
||||
<React.Suspense fallback={<div>Loading...</div>}>
|
||||
<Component {...props} />
|
||||
</React.Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
export default {
|
||||
/**
|
||||
* Only required property. Should be a unique value across all extensions.
|
||||
*/
|
||||
id: 'pdf',
|
||||
getViewportModule() {
|
||||
return ConnectedOHIFDicomPDFViewer;
|
||||
},
|
||||
getSopClassHandlerModule() {
|
||||
return OHIFDicomPDFSopClassHandler;
|
||||
},
|
||||
};
|
||||
Reference in new issue
Block a user