
001package org.hl7.fhir.r5.renderers; 002 003import java.io.IOException; 004import java.io.UnsupportedEncodingException; 005import java.util.ArrayList; 006import java.util.Base64; 007import java.util.HashMap; 008import java.util.List; 009import java.util.Map; 010import java.util.UUID; 011 012import org.hl7.fhir.exceptions.DefinitionException; 013import org.hl7.fhir.exceptions.FHIRException; 014import org.hl7.fhir.exceptions.FHIRFormatError; 015import org.hl7.fhir.r5.model.Attachment; 016import org.hl7.fhir.r5.model.StructureDefinition; 017import org.hl7.fhir.r5.renderers.utils.RenderingContext; 018import org.hl7.fhir.r5.renderers.utils.ResourceWrapper; 019import org.hl7.fhir.r5.utils.EOperationOutcome; 020import org.hl7.fhir.utilities.FileUtilities; 021import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage; 022import org.hl7.fhir.utilities.Utilities; 023import org.hl7.fhir.utilities.filesystem.ManagedFileAccess; 024import org.hl7.fhir.utilities.xhtml.XhtmlNode; 025 026@MarkedToMoveToAdjunctPackage 027public class PatientRenderer extends ResourceRenderer { 028 029 030 public PatientRenderer(RenderingContext context) { 031 super(context); 032 } 033 034 035 @Override 036 public String buildSummary(ResourceWrapper pat) throws UnsupportedEncodingException, IOException { 037 ResourceWrapper id = null; 038 List<ResourceWrapper> list = pat.children("identifier"); 039 for (ResourceWrapper t : list) { 040 id = chooseId(id, t); 041 } 042 list = pat.children("name"); 043 ResourceWrapper n = null; 044 for (ResourceWrapper t : list) { 045 n = chooseName(n, t); 046 } 047 String gender = null; 048 ResourceWrapper item = pat.child("gender"); 049 if (item != null) { 050 gender = context.getTranslatedCode(item.primitiveValue(), "http://hl7.org/fhir/administrative-gender"); 051 } 052 ResourceWrapper dt = pat.child("birthDate"); 053 054 StringBuilder b = new StringBuilder(); 055 if (n != null) { 056 b.append(displayHumanName(n)); 057 } else { 058 b.append(context.formatPhrase(RenderingContext.PAT_NO_NAME)); 059 } 060 b.append(" "); 061 if (item == null) { 062 b.append(context.formatPhrase(RenderingContext.PAT_NO_GENDER)); 063 } else { 064 b.append(gender); 065 } 066 b.append(", "); 067 if (dt == null) { 068 b.append(context.formatPhrase(RenderingContext.PAT_NO_DOB)); 069 } else { 070 b.append(context.formatPhrase(RenderingContext.PAT_DOB, displayDateTime(dt))); 071 } 072 if (id != null) { 073 b.append(" ( "); 074 b.append(displayIdentifier(id)); 075 b.append(")"); 076 } 077 return b.toString(); 078 } 079 080 081 // // name gender DoB (MRN) 082 // public String display(Resource dr) { 083 // Patient pat = (Patient) dr; 084 // Identifier id = null; 085 // for (Identifier t : pat.getIdentifier()) { 086 // id = chooseId(id, t); 087 // } 088 // HumanName n = null; 089 // for (HumanName t : pat.getName()) { 090 // n = chooseName(n, t); 091 // } 092 // return display(n, pat.hasGender() ? context.getTranslatedCode(pat.getGenderElement(), "http://hl7.org/fhir/administrative-gender") : null, pat.getBirthDateElement(), id); 093 // } 094 095 096 private static final int MAX_IMAGE_LENGTH = 2*1024*1024; 097 private static final boolean SHORT = false; 098 099 100 @Override 101 public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper pat) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome { 102 renderResourceTechDetails(pat, x); 103 if (context.isShortPatientForm()) { 104 ResourceWrapper id = null; 105 List<ResourceWrapper> list = pat.children("identifier"); 106 for (ResourceWrapper t : list) { 107 id = chooseId(id, t); 108 } 109 list = pat.children("name"); 110 ResourceWrapper n = null; 111 for (ResourceWrapper t : list) { 112 n = chooseName(n, t); 113 } 114 String gender = null; 115 ResourceWrapper item = pat.child("gender"); 116 if (item != null) { 117 gender = getTranslatedCode(item); 118 } 119 ResourceWrapper dt = pat.child("birthDate"); 120 121 if (n == null) { 122 x.b().tx(context.formatPhrase(RenderingContext.PAT_NO_NAME)); // todo: is this appropriate? 123 } else { 124 renderDataType(status, x.b(), n); 125 } 126 x.tx(" "); 127 if (gender == null) { 128 x.tx(context.formatPhrase(RenderingContext.PAT_NO_GENDER)); 129 } else { 130 x.tx(gender); 131 } 132 x.tx(", "); 133 if (dt == null) { 134 x.tx(context.formatPhrase(RenderingContext.PAT_NO_DOB)); 135 } else { 136 x.tx(context.formatPhrase(RenderingContext.PAT_DOB, displayDateTime(dt))); 137 } 138 if (id != null) { 139 x.tx(" ( "); 140 renderDataType(status, x, id); 141 x.tx(")"); 142 } 143 } else { 144 // banner 145 makeBanner(x.para()).tx(buildSummary(pat)); 146 x.hr(); 147 XhtmlNode tbl; 148 if (hasRenderablePhoto(pat)) { 149 tbl = x.table("none", true); 150 XhtmlNode tr = tbl.tr(); 151 tbl = tr.td().table("grid", false); 152 renderPhoto(tr.td(), pat); 153 } else { 154 tbl = x.table("grid", false); 155 } 156 157 // the table has 4 columns 158 addStatus(status, tbl, pat); 159 addIdentifiers(status, tbl, pat); 160 addNames(status, tbl, pat); 161 addComms(status, tbl, pat); 162 addLangs(status, tbl, pat); 163 addNOKs(status, tbl, pat); 164 addLinks(status, tbl, pat); 165 addExtensions(status, tbl, pat); 166 if (tbl.isEmpty()) { 167 x.remove(tbl); 168 } 169 if (pat.has("contained") && context.isTechnicalMode()) { 170 x.hr(); 171 x.para().b().tx(context.formatMessagePlural(pat.children("contained").size(), RenderingContext.PAT_CONTAINED)); 172 addContained(status, x, pat.children("contained")); 173 } 174 } 175 } 176 177 private ResourceWrapper chooseId(ResourceWrapper oldId, ResourceWrapper newId) { 178 if (oldId == null) { 179 return newId; 180 } 181 if (newId == null) { 182 return oldId; 183 } 184 return isPreferredId(newId.primitiveValue("use"), oldId.primitiveValue("use")) ? newId : oldId; 185 } 186 187 private boolean isPreferredId(String newUse, String oldUse) { 188 if (newUse == null && oldUse == null || newUse == oldUse) { 189 return false; 190 } 191 if (newUse == null) { 192 return true; 193 } 194 switch (newUse) { 195 case "official": return !Utilities.existsInList(oldUse, "usual"); 196 case "old": return !Utilities.existsInList(oldUse, "official", "secondary", "usual"); 197 case "secondary": return !Utilities.existsInList(oldUse, "official", "usual"); 198 case "temp": return !Utilities.existsInList(oldUse, "official", "secondary", "usual"); 199 case "usual": return true; 200 default: return false; 201 } 202 } 203 204 private ResourceWrapper chooseName(ResourceWrapper oldName, ResourceWrapper newName) { 205 if (oldName == null) { 206 return newName; 207 } 208 if (newName == null) { 209 return oldName; 210 } 211 return isPreferredName(newName.primitiveValue("use"), oldName.primitiveValue("use")) ? newName : oldName; 212 } 213 214 215 private boolean isPreferredName(String newUse, String oldUse) { 216 if (newUse == null && oldUse == null || newUse == oldUse) { 217 return false; 218 } 219 if (newUse == null) { 220 return true; 221 } 222 if (oldUse == null) { 223 return Utilities.existsInList(newUse, "official", "usual"); 224 } 225 switch (oldUse) { 226 case "anonymous": return Utilities.existsInList(newUse, "official", "usual"); 227 case "maiden": return Utilities.existsInList(newUse, "official", "usual"); 228 case "nickname": return Utilities.existsInList(newUse, "official", "usual"); 229 case "official": return Utilities.existsInList(newUse, "usual"); 230 case "old": return Utilities.existsInList(newUse, "official", "usual"); 231 case "temp": return Utilities.existsInList(newUse, "official", "usual"); 232 case "usual": return false; 233 } 234 return false; 235 } 236 237 private void addExtensions(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws UnsupportedEncodingException, FHIRException, IOException { 238 Map<String, List<ResourceWrapper>> extensions = new HashMap<>(); 239 List<ResourceWrapper> pw = r.children("extension"); 240 for (ResourceWrapper t : pw) { 241 String url = t.primitiveValue("url"); 242 if (!extensions.containsKey(url)) { 243 extensions.put(url, new ArrayList<>()); 244 } 245 extensions.get(url).add(t); 246 } 247 248 for (String url : extensions.keySet()) { 249 StructureDefinition sd = findCanonical(StructureDefinition.class, url, r); 250 if (sd != null) { 251 List<ResourceWrapper> list = extensions.get(url); 252 boolean anyComplex = false; 253 for (ResourceWrapper ext : list) { 254 anyComplex = anyComplex || ext.has("extension"); 255 } 256 if (!anyComplex) { 257 XhtmlNode tr = tbl.tr(); 258 nameCell(tr, getContext().getTranslated(sd.getTitleElement()), sd.getDescription(), sd.getWebPath()); 259 XhtmlNode td = tr.td(); 260 td.colspan("3"); 261 if (list.size() != 1) { 262 XhtmlNode ul = td.ul(); 263 for (ResourceWrapper s : list) { 264 XhtmlNode li = ul.li(); 265 renderDataType(status, li, s.child("value")); 266 } 267 } else { 268 renderDataType(status, td, list.get(0).child("value")); 269 } 270 } else { 271 for (ResourceWrapper ext : list) { 272 XhtmlNode tr = tbl.tr(); 273 nameCell(tr, sd.getTitle()+":", sd.getDescription()); 274 XhtmlNode td = tr.td(); 275 td.colspan("3"); 276 if (ext.has("extension")) { 277 XhtmlNode ul = td.ul(); 278 for (ResourceWrapper s : ext.extensions()) { 279 XhtmlNode li = ul.li(); 280 li.tx(s.primitiveValue("url")+": "); 281 if (s.has("extension")) { 282 boolean first = true; 283 for (ResourceWrapper t : s.extensions()) { 284 if (first) first = false; else li.tx("; "); 285 li.tx(t.primitiveValue("url")+"="); 286 renderDataType(status, li, t.child("value")); 287 } 288 } else { 289 renderDataType(status, li, s.child("value")); 290 } 291 } 292 } else { 293 renderDataType(status, td, ext.child("value")); 294 } 295 } 296 } 297 } 298 } 299 300 301 } 302 303 private void addIdentifiers(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException { 304 List<ResourceWrapper> ids = r.children("identifier"); 305 ResourceWrapper id = null; 306 for (ResourceWrapper i : ids) { 307 id = chooseId(id, i); 308 } 309 if (id != null) { 310 ids.remove(id); 311 }; 312 if (ids.size() > 0) { 313 XhtmlNode tr = tbl.tr(); 314 nameCell(tr, context.formatMessagePlural(ids.size(), RenderingContext.PAT_OTHER_ID),context.formatMessagePlural(ids.size(), RenderingContext.PAT_OTHER_ID_HINT)); 315 XhtmlNode td = tr.td(); 316 td.colspan("3"); 317 if (ids.size() == 1) { 318 renderDataType(status, td, ids.get(0)); 319 } else { 320 XhtmlNode ul = td.ul(); 321 for (ResourceWrapper i : ids) { 322 renderDataType(status, ul.li(), i); 323 } 324 } 325 } 326 } 327 328 private void addLangs(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException { 329 List<ResourceWrapper> langs = new ArrayList<ResourceWrapper>(); 330 List<ResourceWrapper> comms = r.children("communication"); 331 ResourceWrapper prefLang = null; 332 for (ResourceWrapper t : comms) { 333 ResourceWrapper lang = t.child("language"); 334 if (lang != null) { 335 langs.add(lang); 336 ResourceWrapper l = t.child("preferred"); 337 if (l != null && "true".equals(l.primitiveValue())) { 338 prefLang = lang; 339 } 340 } 341 } 342 if (langs.size() > 0) { 343 XhtmlNode tr = tbl.tr(); 344 nameCell(tr, context.formatMessagePlural(langs.size(), RenderingContext.PAT_LANG), context.formatMessagePlural(langs.size(), RenderingContext.PAT_LANG_HINT)); 345 XhtmlNode td = tr.td(); 346 td.colspan("3"); 347 if (langs.size() == 1) { 348 renderDataType(status, td, langs.get(0)); 349 if (prefLang != null) { 350 td.tx(" "+context.formatPhrase(RenderingContext.PAT_LANG_PREFERRED)); 351 } 352 } else if (langs.size() > 1) { 353 XhtmlNode ul = td.ul(); 354 for (ResourceWrapper i : langs) { 355 XhtmlNode li = ul.li(); 356 renderDataType(status, li, i); 357 if (i == prefLang) { 358 li.tx(" "+context.formatPhrase(RenderingContext.PAT_LANG_PREFERRED));; 359 } 360 } 361 } 362 } 363 } 364 365 366 367 public class NamedReferance { 368 369 private String name; 370 private ResourceWrapper reference; 371 372 public NamedReferance(String name, ResourceWrapper ref) { 373 this.name = name; 374 this.reference = ref; 375 } 376 377 public String getName() { 378 return name; 379 } 380 381 public ResourceWrapper getReference() { 382 return reference; 383 } 384 385 } 386 387 388 private void addLinks(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws UnsupportedEncodingException, FHIRException, IOException { 389 List<NamedReferance> refs = new ArrayList<>(); 390 List<ResourceWrapper> pw = r.children("generalPractitioner"); 391 for (ResourceWrapper t : pw) { 392 refs.add(new NamedReferance(context.formatPhrase(RenderingContext.PAT_GP), t)); 393 } 394 pw = r.children("managingOrganization"); 395 for (ResourceWrapper t : pw) { 396 refs.add(new NamedReferance(context.formatPhrase(RenderingContext.PAT_MO), t)); 397 } 398 pw = r.children("link"); 399 for (ResourceWrapper t : pw) { 400 ResourceWrapper o = t.firstChild("other"); 401 ResourceWrapper l = t.firstChild("type"); 402 if (l != null && o != null) { 403 refs.add(new NamedReferance(describeLinkedRecord(l.primitiveValue()), o)); 404 } 405 } 406 407 if (refs.size() > 0) { 408 XhtmlNode tr = tbl.tr(); 409 nameCell(tr, context.formatPhrase(RenderingContext.PAT_LINKS), context.formatPhrase(RenderingContext.PAT_LINKS_HINT)); 410 XhtmlNode td = tr.td(); 411 td.colspan("3"); 412 XhtmlNode ul = td.ul(); 413 for (NamedReferance ref : refs) { 414 XhtmlNode li = ul.li(); 415 li.tx(ref.getName()); 416 li.tx(": "); 417 renderReference(status, li, ref.getReference()); 418 } 419 } 420 } 421 422 private String describeLinkedRecord(String type) { 423 switch (type) { 424 case "replaced-by" : return context.formatPhrase(RenderingContext.PAT_LINK_REPLBY); 425 case "replaces": return context.formatPhrase(RenderingContext.PAT_LINK_REPL); 426 case "refer": return context.formatPhrase(RenderingContext.PAT_LINK_REFER); 427 case "seealso": return context.formatPhrase(RenderingContext.PAT_LINK_SEE); 428 } 429 return "Unknown"; 430 } 431 432 private void addNOKs(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException { 433 for (ResourceWrapper t : r.children("contact")) { 434 addNOK(status, tbl, r, t); 435 } 436 } 437 438 private void addNOK(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r, ResourceWrapper bw) throws FHIRFormatError, DefinitionException, IOException { 439 List<ResourceWrapper> rels = bw.children("relationship"); 440 ResourceWrapper name = bw.firstChild("name"); 441 ResourceWrapper add = bw.firstChild("address"); 442 String gender = context.getTranslatedCode(bw.primitiveValue("gender"), "http://hl7.org/fhir/administrative-gender"); 443 ResourceWrapper period = bw.firstChild("period"); 444 ResourceWrapper organization = bw.firstChild("organization"); 445 List<ResourceWrapper> tels = bw.children("telecom"); 446 447 if (rels.size() < 2 && name == null && add == null && gender == null && period == null && organization == null && tels.size() == 0) { 448 return; // nothing to render 449 } 450 XhtmlNode tr = tbl.tr(); 451 if (rels.size() == 1) { 452 nameCell(tr, displayDataType(rels.get(0))+":", context.formatPhrase(RenderingContext.PAT_NOM_CONTACT)+" "+displayDataType(rels.get(0))); 453 } else { 454 nameCell(tr, context.formatPhrase(RenderingContext.GENERAL_CONTACT), context.formatPhrase(RenderingContext.PAT_NOK_CONTACT_HINT)); 455 } 456 XhtmlNode td = tr.td(); 457 td.colspan("3"); 458 XhtmlNode ul = td.ul(); 459 XhtmlNode li; 460 if (name != null) { 461 li = ul.li(); 462 renderDataType(status, li, name); 463 if (gender != null) { 464 li.tx(" "+"("+gender+")"); 465 } 466 } else if (gender != null) { 467 li = ul.li(); 468 li.tx(context.formatPhrase(RenderingContext.PAT_GENDER, gender)); 469 } 470 if (rels.size() > 1) { 471 li = ul.li(); 472 li.tx(context.formatPhrase(RenderingContext.PAT_RELN)); 473 boolean first = true; 474 for (ResourceWrapper rel : rels) { 475 if (first) first = false; else li.tx(", "); 476 renderDataType(status, li, rel); 477 } 478 } 479 if (add != null) { 480 renderDataType(status, ul.li(), add); 481 } 482 for (ResourceWrapper cp : tels) { 483 renderDataType(status, ul.li(), cp); 484 } 485 if (organization != null) { 486 li = ul.li(); 487 li.tx(context.formatPhrase(RenderingContext.PAT_ORG)); 488 renderDataType(status, li, organization); 489 } 490 if (period != null) { 491 li = ul.li(); 492 li.tx(context.formatPhrase(RenderingContext.PAT_PERIOD)); 493 renderDataType(status, li, period); 494 } 495 } 496 497 private void addNames(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException { 498 List<ResourceWrapper> names = r.children("name"); 499 ResourceWrapper name = null; 500 for (ResourceWrapper n : names) { 501 name = chooseName(name, n); 502 } 503 if (name != null) { 504 names.remove(name); 505 }; 506 if (names.size() == 1) { 507 XhtmlNode tr = tbl.tr(); 508 nameCell(tr, context.formatPhrase(RenderingContext.PAT_ALT_NAME), context.formatPhrase(RenderingContext.PAT_ALT_NAME_HINT)); 509 XhtmlNode td = tr.td(); 510 td.colspan("3"); 511 if (names.size() == 1) { 512 renderDataType(status, td, names.get(0)); 513 } else { 514 XhtmlNode ul = td.ul(); 515 for (ResourceWrapper n : names) { 516 renderDataType(status, ul.li(), n); 517 } 518 } 519 } 520 } 521 522 private void addComms(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException { 523 List<ResourceWrapper> tels = r.children("telecom"); 524 List<ResourceWrapper> adds = r.children("address"); 525 if (tels.size() + adds.size() > 0) { 526 XhtmlNode tr = tbl.tr(); 527 nameCell(tr, context.formatPhrase(RenderingContext.PAT_CONTACT), context.formatPhrase(RenderingContext.PAT_CONTACT_HINT)); 528 XhtmlNode td = tr.td(); 529 td.colspan("3"); 530 if (tels.size() + adds.size() == 1) { 531 if (adds.isEmpty()) { 532 renderDataType(status, td, tels.get(0)); 533 } else { 534 renderDataType(status, td, adds.get(0)); 535 } 536 } else { 537 XhtmlNode ul = td.ul(); 538 for (ResourceWrapper n : tels) { 539 renderDataType(status, ul.li(), n); 540 } 541 for (ResourceWrapper n : adds) { 542 renderDataType(status, ul.li(), n); 543 } 544 } 545 } 546 } 547 548 private void addStatus(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, UnsupportedEncodingException, FHIRException, IOException { 549 // TODO Auto-generated method stub 550 int count = 0; 551 if (r.has("active")) { 552 count++; 553 } 554 if (r.has("deceased")) { 555 count++; 556 } 557 if (r.has("maritalStatus")) { 558 count++; 559 } 560 if (r.has("multipleBirth")) { 561 count++; 562 } 563 if (count > 0) { 564 XhtmlNode tr = tbl.tr(); 565 int pos = 0; 566 if (r.has("active")) { 567 List<ResourceWrapper> a = r.children("active"); 568 if (!a.isEmpty()) { 569 pos++; 570 nameCell(tr, context.formatPhrase(RenderingContext.PAT_ACTIVE), context.formatPhrase(RenderingContext.PAT_ACTIVE_HINT)); 571 XhtmlNode td = tr.td(); 572 if (pos == count) { 573 td.colspan("3"); 574 } 575 renderDataType(status, td, a.get(0)); 576 } 577 } 578 if (r.has("deceased[x]")) { 579 List<ResourceWrapper> a = r.children("deceased[x]"); 580 if (!a.isEmpty()) { 581 pos++; 582 nameCell(tr, context.formatPhrase(RenderingContext.PAT_DECEASED), context.formatPhrase(RenderingContext.PAT_DECEASED_HINT)); 583 XhtmlNode td = tr.td(); 584 if (pos == count) { 585 td.colspan("3"); 586 } 587 renderDataType(status, td, a.get(0)); 588 } 589 } 590 if (r.has("maritalStatus")) { 591 List<ResourceWrapper> a = r.children("maritalStatus"); 592 if (!a.isEmpty()) { 593 pos++; 594 if (pos == 3) { 595 tr = tbl.tr(); 596 } 597 nameCell(tr, context.formatPhrase(RenderingContext.PAT_MARITAL), context.formatPhrase(RenderingContext.PAT_MARITAL_HINT)); 598 XhtmlNode td = tr.td(); 599 if (pos == count) { 600 td.colspan("3"); 601 } 602 renderDataType(status, td, a.get(0)); 603 } 604 } 605 if (r.has("multipleBirth[x]")) { 606 List<ResourceWrapper> a = r.children("multipleBirth[x]"); 607 if (!a.isEmpty()) { 608 pos++; 609 if (pos == 3) { 610 tr = tbl.tr(); 611 } 612 nameCell(tr, context.formatPhrase(RenderingContext.PAT_MUL_BIRTH), context.formatPhrase(RenderingContext.PAT_MUL_BIRTH_HINT)); 613 XhtmlNode td = tr.td(); 614 if (pos == count) { 615 td.colspan("3"); 616 } 617 renderDataType(status, td, a.get(0)); 618 } 619 } 620 } 621 } 622 623 private void nameCell(XhtmlNode tr, String text, String title) { 624 XhtmlNode td = tr.td(); 625 td.setAttribute("title", title); 626 td.tx(text); 627 td.style("background-color: #f3f5da"); 628 } 629 630 private void nameCell(XhtmlNode tr, String text, String title, String link) { 631 XhtmlNode td = tr.td(); 632 td.setAttribute("title", title); 633 if (link != null) { 634 td.ah(context.prefixLocalHref(link)).tx(text); 635 } else { 636 td.tx(text); 637 } 638 td.style("background-color: #f3f5da"); 639 } 640 641 private void renderPhoto(XhtmlNode td, ResourceWrapper r) throws UnsupportedEncodingException, FHIRException, IOException { 642 if (r.has("photo")) { 643 List<ResourceWrapper> a = r.children("photo"); 644 for (ResourceWrapper att : a) { 645 String ct = att.primitiveValue("contentType"); 646 byte[] cnt = att.has("data") ? Base64.getDecoder().decode(att.primitiveValue("data")) : null; 647 if (ct.startsWith("image/") && 648 cnt != null && (!context.isInlineGraphics() || (cnt.length > 0 && cnt.length < MAX_IMAGE_LENGTH))) { 649 String ext = extensionForType(ct); 650 if (context.isInlineGraphics() || Utilities.noString(context.getDestDir()) || ext == null) { 651 td.img("data:"+ct+";base64,"+att.primitiveValue("data"), "patient photo"); 652 } else { 653 String n = UUID.randomUUID().toString().toLowerCase()+ext; 654 FileUtilities.bytesToFile(cnt, ManagedFileAccess.file(Utilities.path(context.getDestDir(), n))); 655 context.registerFile(n); 656 td.img(n, context.formatPhrase(RenderingContext.PAT_PHOTO)); 657 } 658 return; 659 } 660 } 661 } 662 return; 663 } 664 665 private String extensionForType(String contentType) { 666 if (contentType.equals("image/gif")) { 667 return ".gif"; 668 } 669 if (contentType.equals("image/png")) { 670 return ".png"; 671 } 672 if (contentType.equals("image/jpeg")) { 673 return ".jpg"; 674 } 675 return null; 676 } 677 678 private boolean hasRenderablePhoto(ResourceWrapper r) throws UnsupportedEncodingException, FHIRException, IOException { 679 if (r.has("photo")) { 680 List<ResourceWrapper> a = r.children("photo"); 681 for (ResourceWrapper att : a) { 682 if (att.has("contentType") && att.primitiveValue("contentType").startsWith("image/") && 683 att.has("data") && (!context.isInlineGraphics() || (att.primitiveValue("data").length() > 0 && 684 att.primitiveValue("data").length() < MAX_IMAGE_LENGTH))) { 685 return true; 686 } 687 } 688 } 689 return false; 690 } 691 692 private XhtmlNode makeBanner(XhtmlNode para) { 693 para.style("border: 1px #661aff solid; background-color: #e6e6ff; padding: 10px;"); 694 return para; 695 } 696}