You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
488 lines
13 KiB
488 lines
13 KiB
2 years ago
|
<?php
|
||
|
|
||
|
/**
|
||
|
* Dicom viewer wrapper script for documents
|
||
|
*
|
||
|
* @package OpenEMR
|
||
|
* @link https://www.open-emr.org
|
||
|
* @author Victor Kofia <https://kofiav.com> 'Viewer'
|
||
|
* @author Jerry Padgett <sjpadgett@gmail.com> 'Viewer wrapper'
|
||
|
* @copyright Copyright (c) 2017-2018 Victor Kofia <https://kofiav.com>
|
||
|
* @copyright Copyright (c) 2018-2020 Jerry Padgett <sjpadgett@gmail.com>
|
||
|
* @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
|
||
|
*/
|
||
|
|
||
|
/* Warning: This script wraps the Dicom viewer which is HTML5 compatible only and bootstrap styling
|
||
|
* should not be used inside this script due to style conflicts with viewer, namely, hidden class.
|
||
|
*/
|
||
|
|
||
|
require_once('../interface/globals.php');
|
||
|
|
||
|
use OpenEMR\Common\Acl\AclMain;
|
||
|
use OpenEMR\Common\Csrf\CsrfUtils;
|
||
|
use OpenEMR\Common\Twig\TwigContainer;
|
||
|
use OpenEMR\Core\Header;
|
||
|
|
||
|
if (!AclMain::aclCheckCore('patients', 'docs')) {
|
||
|
echo (new TwigContainer(null, $GLOBALS['kernel']))->getTwig()->render('core/unauthorized.html.twig', ['pageTitle' => xl("Dicom Viewer")]);
|
||
|
exit;
|
||
|
}
|
||
|
|
||
|
$web_path = $_REQUEST['web_path'] ?? null;
|
||
|
if ($web_path) {
|
||
|
$patid = $_REQUEST['patient_id'] ?? null;
|
||
|
$docid = isset($_REQUEST['document_id']) ? $_REQUEST['document_id'] : ($_REQUEST['doc_id'] ?? null);
|
||
|
$d = new Document(attr($docid));
|
||
|
$type = '.dcm';
|
||
|
if ($d->get_mimetype() == 'application/dicom+zip') {
|
||
|
$type = '.zip';
|
||
|
}
|
||
|
$csrf = attr(CsrfUtils::collectCsrfToken());
|
||
|
$state_url = $GLOBALS['web_root'] . "/library/ajax/upload.php";
|
||
|
$web_path = attr($web_path) . '&retrieve&patient_id=' . attr_url($patid) . '&document_id=' . attr_url($docid) . '&as_file=false&type=' . attr_url($type);
|
||
|
}
|
||
|
?>
|
||
|
<!DOCTYPE html>
|
||
|
<html>
|
||
|
<head>
|
||
|
<title><?php echo xlt("Dicom Viewer"); ?></title>
|
||
|
|
||
|
<?php Header::setupHeader(['dwv', 'i18next', 'i18next-xhr-backend', 'i18next-browser-languagedetector', 'jszip', 'magic-wand', 'konva']); ?>
|
||
|
<script type="text/javascript" src="<?php echo $GLOBALS['assets_static_relative']; ?>/flot/dist/es5/jquery.flot.js"></script>
|
||
|
<!-- Local (dwv) -->
|
||
|
<script src="<?php echo $GLOBALS['web_root'] ?>/library/js/dwv/gui/browser.js"></script>
|
||
|
<script src="<?php echo $GLOBALS['web_root'] ?>/library/js/dwv/gui/colourMap.js"></script>
|
||
|
<script src="<?php echo $GLOBALS['web_root'] ?>/library/js/dwv/gui/custom.js"></script>
|
||
|
<script src="<?php echo $GLOBALS['web_root'] ?>/library/js/dwv/gui/dropboxLoader.js"></script>
|
||
|
<script src="<?php echo $GLOBALS['web_root'] ?>/library/js/dwv/gui/filter.js"></script>
|
||
|
<script src="<?php echo $GLOBALS['web_root'] ?>/library/js/dwv/gui/generic.js"></script>
|
||
|
<script src="<?php echo $GLOBALS['web_root'] ?>/library/js/dwv/gui/undo.js"></script>
|
||
|
<script src="<?php echo $GLOBALS['web_root'] ?>/library/js/dwv/gui/help.js"></script>
|
||
|
<script src="<?php echo $GLOBALS['web_root'] ?>/library/js/dwv/gui/html.js"></script>
|
||
|
<script src="<?php echo $GLOBALS['web_root'] ?>/library/js/dwv/gui/infoController.js"></script>
|
||
|
<script src="<?php echo $GLOBALS['web_root'] ?>/library/js/dwv/gui/infoOverlay.js"></script>
|
||
|
<script src="<?php echo $GLOBALS['web_root'] ?>/library/js/dwv/gui/loader.js"></script>
|
||
|
<script src="<?php echo $GLOBALS['web_root'] ?>/library/js/dwv/gui/tools.js"></script>
|
||
|
<script src="<?php echo $GLOBALS['web_root'] ?>/library/js/dwv/gui/plot.js"></script>
|
||
|
<!-- i18n dwv wrapper -->
|
||
|
<script src="<?php echo $GLOBALS['web_root'] ?>/library/js/dwv/dwv_i18n.js"></script>
|
||
|
<!-- Launch the app -->
|
||
|
<script src="<?php echo $GLOBALS['web_root'] ?>/library/js/dwv/dicom_gui.js"></script>
|
||
|
<script src="<?php echo $GLOBALS['web_root'] ?>/library/js/dwv/dicom_launcher.js"></script>
|
||
|
</head>
|
||
|
<style type="text/css">
|
||
|
body {
|
||
|
background-color: #555;
|
||
|
color: #fff;
|
||
|
}
|
||
|
|
||
|
button, input, li, table {
|
||
|
margin-top: 0.2em;
|
||
|
}
|
||
|
|
||
|
li button, li input {
|
||
|
margin: 0;
|
||
|
}
|
||
|
|
||
|
.container-fluid {
|
||
|
display: flex;
|
||
|
flex-direction: column;
|
||
|
}
|
||
|
|
||
|
#pageHeader {
|
||
|
display: flex;
|
||
|
flex-direction: row;
|
||
|
justify-content: space-between;
|
||
|
padding: .175rem;
|
||
|
}
|
||
|
|
||
|
#pageHeader h1, h2, h3, h5, h6 {
|
||
|
margin: 0;
|
||
|
color: #fff;
|
||
|
}
|
||
|
|
||
|
#pageHeader a {
|
||
|
color: #ddf;
|
||
|
}
|
||
|
|
||
|
#pageHeader .toolbar {
|
||
|
}
|
||
|
|
||
|
#pageMain {
|
||
|
display: flex;
|
||
|
flex-direction: row;
|
||
|
justify-content: center;
|
||
|
}
|
||
|
|
||
|
/* Layers */
|
||
|
.layerContainer {
|
||
|
position: relative;
|
||
|
margin: .625rem;
|
||
|
width: 512px;
|
||
|
height: 512px;
|
||
|
}
|
||
|
|
||
|
.imageLayer {
|
||
|
position: absolute;
|
||
|
}
|
||
|
|
||
|
.drawDiv {
|
||
|
position: absolute;
|
||
|
pointer-events: none;
|
||
|
}
|
||
|
|
||
|
/* drag&drop */
|
||
|
.dropBox {
|
||
|
}
|
||
|
|
||
|
.dropBoxBorder {
|
||
|
margin: 20px auto;
|
||
|
border: 5px dashed #ccc;
|
||
|
}
|
||
|
|
||
|
.dropBoxBorder.hover {
|
||
|
margin: 20px auto;
|
||
|
border: 5px dashed #cc0;
|
||
|
}
|
||
|
|
||
|
/* toolbar */
|
||
|
.toolList ul {
|
||
|
padding: 0;
|
||
|
}
|
||
|
|
||
|
.toolList li {
|
||
|
list-style-type: none;
|
||
|
}
|
||
|
|
||
|
/* info */
|
||
|
.infoLayer ul {
|
||
|
margin: 0;
|
||
|
padding: 2px;
|
||
|
list-style-type: none;
|
||
|
}
|
||
|
|
||
|
.infoLayer li {
|
||
|
margin-top: 0;
|
||
|
}
|
||
|
|
||
|
.infoLayer canvas {
|
||
|
margin: 0;
|
||
|
padding: 2px;
|
||
|
}
|
||
|
|
||
|
.info {
|
||
|
color: #cde;
|
||
|
text-shadow: 1px 1px #000;
|
||
|
font-size: 80%;
|
||
|
}
|
||
|
|
||
|
.infoc {
|
||
|
color: #ff0;
|
||
|
text-shadow: 1px 1px #000;
|
||
|
font-size: 120%;
|
||
|
}
|
||
|
|
||
|
.infotl {
|
||
|
position: absolute;
|
||
|
top: 0;
|
||
|
left: 0;
|
||
|
text-align: left;
|
||
|
text-shadow: 0 1px 0 #000;
|
||
|
}
|
||
|
|
||
|
.infotc {
|
||
|
position: absolute;
|
||
|
top: 0;
|
||
|
left: 50%;
|
||
|
right: 50%;
|
||
|
text-align: center;
|
||
|
text-shadow: 0 1px 0 #000;
|
||
|
}
|
||
|
|
||
|
.infotr {
|
||
|
position: absolute;
|
||
|
top: 0;
|
||
|
right: 0;
|
||
|
text-align: right;
|
||
|
text-shadow: 0 1px 0 #000;
|
||
|
}
|
||
|
|
||
|
.infocl {
|
||
|
position: absolute;
|
||
|
bottom: 50%;
|
||
|
left: 0;
|
||
|
text-align: left;
|
||
|
text-shadow: 0 1px 0 #000;
|
||
|
}
|
||
|
|
||
|
.infocr {
|
||
|
position: absolute;
|
||
|
bottom: 50%;
|
||
|
right: 2px;
|
||
|
text-align: right;
|
||
|
text-shadow: 0 1px 0 #000;
|
||
|
}
|
||
|
|
||
|
.infobl {
|
||
|
position: absolute;
|
||
|
bottom: 0;
|
||
|
left: 0;
|
||
|
text-align: left;
|
||
|
text-shadow: 0 1px 0 #000;
|
||
|
}
|
||
|
|
||
|
.infobc {
|
||
|
position: absolute;
|
||
|
bottom: 0;
|
||
|
left: 50%;
|
||
|
right: 50%;
|
||
|
text-align: center;
|
||
|
text-shadow: 0 1px 0 #000;
|
||
|
}
|
||
|
|
||
|
.infobr {
|
||
|
position: absolute;
|
||
|
bottom: 0;
|
||
|
right: 0;
|
||
|
text-align: right;
|
||
|
text-shadow: 0 1px 0 #000;
|
||
|
}
|
||
|
|
||
|
.plot {
|
||
|
position: absolute;
|
||
|
width: 100px;
|
||
|
height: 50px;
|
||
|
bottom: 15px;
|
||
|
right: 0;
|
||
|
}
|
||
|
|
||
|
/* tag list */
|
||
|
table.tagsTable {
|
||
|
border-collapse: collapse;
|
||
|
background-color: #fff;
|
||
|
color: #000
|
||
|
}
|
||
|
|
||
|
table.tagsTable thead {
|
||
|
background-color: #000;
|
||
|
color: #fff
|
||
|
}
|
||
|
|
||
|
table.tagsTable thead th {
|
||
|
text-transform: uppercase;
|
||
|
font-weight: bold;
|
||
|
opacity: 0.9;
|
||
|
}
|
||
|
|
||
|
table.tagsTable tr:nth-child(even) {
|
||
|
background-color: #c2c2c2;
|
||
|
}
|
||
|
|
||
|
table.drawsTable {
|
||
|
border-collapse: collapse;
|
||
|
}
|
||
|
|
||
|
table.drawsTable td {
|
||
|
vertical-align: middle;
|
||
|
}
|
||
|
|
||
|
table.drawsTable thead th {
|
||
|
text-transform: uppercase;
|
||
|
font-weight: bold;
|
||
|
opacity: 0.5;
|
||
|
}
|
||
|
|
||
|
.highlighted {
|
||
|
background: #feeeac;
|
||
|
}
|
||
|
|
||
|
.tags {
|
||
|
background-color: #fff;
|
||
|
color: #000;
|
||
|
padding: 1rem 1.25rem;
|
||
|
}
|
||
|
|
||
|
.tags form {
|
||
|
width: 45%;
|
||
|
}
|
||
|
|
||
|
/* draw list */
|
||
|
.drawList tr:nth-child(even) {
|
||
|
background-color: #333;
|
||
|
}
|
||
|
|
||
|
/* history list */
|
||
|
div.history {
|
||
|
display: none;
|
||
|
}
|
||
|
|
||
|
.history_list {
|
||
|
width: 100%;
|
||
|
}
|
||
|
|
||
|
/* help */
|
||
|
.help {
|
||
|
padding: 1rem 1.25rem;
|
||
|
}
|
||
|
|
||
|
.popup {
|
||
|
position: absolute;
|
||
|
top: 15%;
|
||
|
left: 15%;
|
||
|
visibility: hidden;
|
||
|
transform: scale(1.05);
|
||
|
transition: visibility 0s linear 0.25s, opacity 0.25s 0s, transform 0.25s;
|
||
|
z-index: 1000;
|
||
|
}
|
||
|
|
||
|
#loaderlist.popup {
|
||
|
top: 50%;
|
||
|
left: 50%;
|
||
|
z-index: 1010;
|
||
|
}
|
||
|
|
||
|
.loaderSelect {
|
||
|
display: inline;
|
||
|
width: 50%;
|
||
|
}
|
||
|
|
||
|
#loaderlist div.popup-content {
|
||
|
position: absolute;
|
||
|
top: 50%;
|
||
|
left: 50%;
|
||
|
transform: translate(-50%, -50%);
|
||
|
background-color: white;
|
||
|
padding: 1rem 1.25rem;
|
||
|
width: 24rem;
|
||
|
border-radius: 0.5rem;
|
||
|
padding-left: .325rem;
|
||
|
}
|
||
|
|
||
|
.lg div.popup-content {
|
||
|
position: static;
|
||
|
border: 2px solid #fff;
|
||
|
width: 75vw;
|
||
|
border-radius: 0.5rem;
|
||
|
overflow: auto;
|
||
|
height: 75vh;
|
||
|
background-color: white;
|
||
|
}
|
||
|
|
||
|
#helpPopup div.popup-content {
|
||
|
background-color: black;
|
||
|
}
|
||
|
|
||
|
.close-button {
|
||
|
background: #ff5d5a;
|
||
|
position: absolute;
|
||
|
line-height: 1.5rem;
|
||
|
text-align: center;
|
||
|
right: 20px;
|
||
|
width: 1.5rem;
|
||
|
height: 1.5rem;
|
||
|
cursor: pointer;
|
||
|
z-index: 1011;
|
||
|
}
|
||
|
|
||
|
.close-button:hover {
|
||
|
background-color: lightcoral;
|
||
|
}
|
||
|
|
||
|
.show-popup {
|
||
|
opacity: 1;
|
||
|
visibility: visible;
|
||
|
transform: scale(1.0);
|
||
|
transition: visibility 0s linear 0s, opacity 0.25s 0s, transform 0.25s;
|
||
|
}
|
||
|
|
||
|
#progressbar {
|
||
|
width: 0;
|
||
|
max-width: 92%;
|
||
|
height: 1.25rem;
|
||
|
background-color: #4CAF50;
|
||
|
}
|
||
|
|
||
|
#pageHeader h2 em {
|
||
|
font-size: 14px;
|
||
|
color: red;
|
||
|
}
|
||
|
|
||
|
div.toolList {
|
||
|
margin: .325rem 0 .325rem;
|
||
|
}
|
||
|
</style>
|
||
|
<body>
|
||
|
<!-- DWV -->
|
||
|
<div id="dwv" class="container-fluid" src='<?php echo $web_path ?>'>
|
||
|
<?php if ($web_path) { ?>
|
||
|
<input type="hidden" id="state_url" value='<?php echo $state_url ?>' />
|
||
|
<input type="hidden" id="csrf" value='<?php echo $csrf ?>' />
|
||
|
<input type="hidden" id="doc_id" value='<?php echo attr($docid) ?>'' />
|
||
|
<?php } ?>
|
||
|
<div id="pageHeader">
|
||
|
<!-- Title -->
|
||
|
<h2>DICOM Viewer<span> <em>( Not for Diagnostics )</em></h2>
|
||
|
</span>
|
||
|
<span class="editspan"></span>
|
||
|
<div class="toolbar"></div>
|
||
|
</div><!-- /pageHeader -->
|
||
|
<div id="pageMain">
|
||
|
<!-- get state json data if file -->
|
||
|
<div class="openData" title="File">
|
||
|
<!-- Open image files -->
|
||
|
<div class="popup" id="loaderlist">
|
||
|
<div class="popup-content">
|
||
|
<span class="close-button" onclick="toggle('loaderlist');">×</span>
|
||
|
<br />
|
||
|
<div class="openData" title="File">
|
||
|
<div class="loaderlist"></div>
|
||
|
<div id="progressbar"></div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
<!-- Toolbox -->
|
||
|
<div class="toolList" title="Toolbox"></div>
|
||
|
<!-- History -->
|
||
|
<div class="history" title="History"></div>
|
||
|
<!-- Tags -->
|
||
|
<div class="popup lg" id="tagsPopup">
|
||
|
<div class="popup-content">
|
||
|
<span class="close-button" onclick="toggle('tagsPopup');">×</span>
|
||
|
<div class="tags" title="Tags">Please load a DICOM study to view image tags.</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
<!-- Help -->
|
||
|
<div class="popup lg" id="helpPopup">
|
||
|
<div class="popup-content">
|
||
|
<span class="close-button" onclick="toggle('helpPopup');">×</span>
|
||
|
<div class="help" title="Help"></div>
|
||
|
</div>
|
||
|
</div>
|
||
|
<!-- Layer Container -->
|
||
|
<div class="layerDialog" title="Image">
|
||
|
<div class="layerContainer">
|
||
|
<div class="dropBox dropBoxBorder"></div>
|
||
|
<canvas class="imageLayer">Only for HTML5 compatible browsers...</canvas>
|
||
|
<div class="drawDiv"></div>
|
||
|
<div class="infoLayer">
|
||
|
<div class="infotl"></div>
|
||
|
<div class="infotc"></div>
|
||
|
<div class="infotr"></div>
|
||
|
<div class="infocl"></div>
|
||
|
<div class="infocr"></div>
|
||
|
<div class="infobl"></div>
|
||
|
<div class="infobc"></div>
|
||
|
<div class="infobr" style="bottom: 64px;"></div>
|
||
|
<div class="plot"></div>
|
||
|
</div><!-- /infoLayer -->
|
||
|
</div><!-- /layerContainer -->
|
||
|
</div><!-- /layerDialog -->
|
||
|
<!-- DrawList -->
|
||
|
<div class="drawList" title="Draw list"></div>
|
||
|
</div><!-- /pageMain -->
|
||
|
</div><!-- /dwv -->
|
||
|
|
||
|
</body>
|
||
|
</html>
|