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