
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, xlinkNarrative(x.b(), n), n); 125 } 126 x.tx(" "); 127 if (gender == null) { 128 x.tx(context.formatPhrase(RenderingContext.PAT_NO_GENDER)); 129 } else { 130 spanIfTracking(x, pat.child("gender")).tx(gender); 131 } 132 x.tx(", "); 133 if (dt == null) { 134 x.tx(context.formatPhrase(RenderingContext.PAT_NO_DOB)); 135 } else { 136 spanIfTracking(x, dt).tx(context.formatPhrase(RenderingContext.PAT_DOB, displayDateTime(dt))); 137 } 138 if (id != null) { 139 x.tx(" ( "); 140 renderDataType(status, spanIfTracking(x, id), id); 141 x.tx(")"); 142 } 143 } else { 144 // banner 145 makeBanner(x.para(), pat).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, xlinkNarrative(li, s.child("value")), s.child("value")); 266 } 267 } else { 268 renderDataType(status, xlinkNarrative(td, list.get(0).child("value")), 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, xlinkNarrative(li, t.child("value")), t.child("value")); 287 } 288 } else { 289 renderDataType(status, xlinkNarrative(li, s.child("value")), s.child("value")); 290 } 291 } 292 } else { 293 renderDataType(status, xlinkNarrative(td, ext.child("value")), 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, xlinkNarrative(td, ids.get(0)), ids.get(0)); 319 } else { 320 XhtmlNode ul = td.ul(); 321 for (ResourceWrapper i : ids) { 322 renderDataType(status, xlinkNarrative(ul.li(), i), 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, xlinkNarrative(td, langs.get(0)), 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, xlinkNarrative(li, i), 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 type; 371 private ResourceWrapper reference; 372 373 public NamedReferance(String name, ResourceWrapper type, ResourceWrapper ref) { 374 this.name = name; 375 this.type = type; 376 this.reference = ref; 377 } 378 379 public String getName() { 380 return name; 381 } 382 383 public ResourceWrapper getReference() { 384 return reference; 385 } 386 387 public ResourceWrapper getType() { 388 return type; 389 } 390 391 } 392 393 394 private void addLinks(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws UnsupportedEncodingException, FHIRException, IOException { 395 List<NamedReferance> refs = new ArrayList<>(); 396 List<ResourceWrapper> pw = r.children("generalPractitioner"); 397 for (ResourceWrapper t : pw) { 398 refs.add(new NamedReferance(context.formatPhrase(RenderingContext.PAT_GP), null, t)); 399 } 400 pw = r.children("managingOrganization"); 401 for (ResourceWrapper t : pw) { 402 refs.add(new NamedReferance(context.formatPhrase(RenderingContext.PAT_MO), null, t)); 403 } 404 pw = r.children("link"); 405 for (ResourceWrapper t : pw) { 406 ResourceWrapper o = t.firstChild("other"); 407 ResourceWrapper l = t.firstChild("type"); 408 if (l != null && o != null) { 409 refs.add(new NamedReferance(describeLinkedRecord(l.primitiveValue()), l, o)); 410 } 411 } 412 413 if (refs.size() > 0) { 414 XhtmlNode tr = tbl.tr(); 415 nameCell(tr, context.formatPhrase(RenderingContext.PAT_LINKS), context.formatPhrase(RenderingContext.PAT_LINKS_HINT)); 416 XhtmlNode td = tr.td(); 417 td.colspan("3"); 418 XhtmlNode ul = td.ul(); 419 for (NamedReferance ref : refs) { 420 XhtmlNode li = ul.li(); 421 if (ref.getType() != null) { 422 spanIfTracking(li, ref.getType()).tx(ref.getName()); 423 } else { 424 li.tx(ref.getName()); 425 } 426 li.tx(": "); 427 renderReference(status, li, ref.getReference()); 428 } 429 } 430 } 431 432 private String describeLinkedRecord(String type) { 433 switch (type) { 434 case "replaced-by" : return context.formatPhrase(RenderingContext.PAT_LINK_REPLBY); 435 case "replaces": return context.formatPhrase(RenderingContext.PAT_LINK_REPL); 436 case "refer": return context.formatPhrase(RenderingContext.PAT_LINK_REFER); 437 case "seealso": return context.formatPhrase(RenderingContext.PAT_LINK_SEE); 438 } 439 return "Unknown"; 440 } 441 442 private void addNOKs(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException { 443 for (ResourceWrapper t : r.children("contact")) { 444 addNOK(status, tbl, r, t); 445 } 446 } 447 448 private void addNOK(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r, ResourceWrapper bw) throws FHIRFormatError, DefinitionException, IOException { 449 List<ResourceWrapper> rels = bw.children("relationship"); 450 ResourceWrapper name = bw.firstChild("name"); 451 ResourceWrapper add = bw.firstChild("address"); 452 String gender = context.getTranslatedCode(bw.primitiveValue("gender"), "http://hl7.org/fhir/administrative-gender"); 453 ResourceWrapper period = bw.firstChild("period"); 454 ResourceWrapper organization = bw.firstChild("organization"); 455 List<ResourceWrapper> tels = bw.children("telecom"); 456 457 if (rels.size() < 2 && name == null && add == null && gender == null && period == null && organization == null && tels.size() == 0) { 458 return; // nothing to render 459 } 460 XhtmlNode tr = tbl.tr(); 461 if (rels.size() == 1) { 462 nameCell(tr, displayDataType(rels.get(0))+":", context.formatPhrase(RenderingContext.PAT_NOM_CONTACT)+" "+displayDataType(rels.get(0))); 463 } else { 464 nameCell(tr, context.formatPhrase(RenderingContext.GENERAL_CONTACT), context.formatPhrase(RenderingContext.PAT_NOK_CONTACT_HINT)); 465 } 466 XhtmlNode td = tr.td(); 467 td.colspan("3"); 468 XhtmlNode ul = td.ul(); 469 XhtmlNode li; 470 if (name != null) { 471 li = ul.li(); 472 renderDataType(status, xlinkNarrative(li, name), name); 473 if (gender != null) { 474 li.tx(" "+"("+gender+")"); 475 } 476 } else if (gender != null) { 477 li = ul.li(); 478 li.tx(context.formatPhrase(RenderingContext.PAT_GENDER, gender)); 479 } 480 if (rels.size() > 1) { 481 li = ul.li(); 482 li.tx(context.formatPhrase(RenderingContext.PAT_RELN)); 483 boolean first = true; 484 for (ResourceWrapper rel : rels) { 485 if (first) first = false; else li.tx(", "); 486 renderDataType(status, xlinkNarrative(li, rel), rel); 487 } 488 } 489 if (add != null) { 490 renderDataType(status, xlinkNarrative(ul.li(), add), add); 491 } 492 for (ResourceWrapper cp : tels) { 493 renderDataType(status, xlinkNarrative(ul.li(), cp), cp); 494 } 495 if (organization != null) { 496 li = ul.li(); 497 li.tx(context.formatPhrase(RenderingContext.PAT_ORG)); 498 renderDataType(status, xlinkNarrative(li, organization), organization); 499 } 500 if (period != null) { 501 li = ul.li(); 502 li.tx(context.formatPhrase(RenderingContext.PAT_PERIOD)); 503 renderDataType(status, xlinkNarrative(li, period), period); 504 } 505 } 506 507 private void addNames(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException { 508 List<ResourceWrapper> names = r.children("name"); 509 ResourceWrapper name = null; 510 for (ResourceWrapper n : names) { 511 name = chooseName(name, n); 512 } 513 if (name != null) { 514 names.remove(name); 515 }; 516 if (names.size() == 1) { 517 XhtmlNode tr = tbl.tr(); 518 nameCell(tr, context.formatPhrase(RenderingContext.PAT_ALT_NAME), context.formatPhrase(RenderingContext.PAT_ALT_NAME_HINT)); 519 XhtmlNode td = tr.td(); 520 td.colspan("3"); 521 if (names.size() == 1) { 522 renderDataType(status, xlinkNarrative(td, names.get(0)), names.get(0)); 523 } else { 524 XhtmlNode ul = td.ul(); 525 for (ResourceWrapper n : names) { 526 renderDataType(status,xlinkNarrative(ul.li(), n), n); 527 } 528 } 529 } 530 } 531 532 private void addComms(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException { 533 List<ResourceWrapper> tels = r.children("telecom"); 534 List<ResourceWrapper> adds = r.children("address"); 535 if (tels.size() + adds.size() > 0) { 536 XhtmlNode tr = tbl.tr(); 537 nameCell(tr, context.formatPhrase(RenderingContext.PAT_CONTACT), context.formatPhrase(RenderingContext.PAT_CONTACT_HINT)); 538 XhtmlNode td = tr.td(); 539 td.colspan("3"); 540 if (tels.size() + adds.size() == 1) { 541 if (adds.isEmpty()) { 542 renderDataType(status, xlinkNarrative(td, tels.get(0)), tels.get(0)); 543 } else { 544 renderDataType(status, xlinkNarrative(td, adds.get(0)), adds.get(0)); 545 } 546 } else { 547 XhtmlNode ul = td.ul(); 548 for (ResourceWrapper n : tels) { 549 renderDataType(status, xlinkNarrative(ul.li(), n), n); 550 } 551 for (ResourceWrapper n : adds) { 552 renderDataType(status, xlinkNarrative(ul.li(), n), n); 553 } 554 } 555 } 556 } 557 558 private void addStatus(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, UnsupportedEncodingException, FHIRException, IOException { 559 // TODO Auto-generated method stub 560 int count = 0; 561 if (r.has("active")) { 562 count++; 563 } 564 if (r.has("deceased")) { 565 count++; 566 } 567 if (r.has("maritalStatus")) { 568 count++; 569 } 570 if (r.has("multipleBirth")) { 571 count++; 572 } 573 if (count > 0) { 574 XhtmlNode tr = tbl.tr(); 575 int pos = 0; 576 if (r.has("active")) { 577 List<ResourceWrapper> a = r.children("active"); 578 if (!a.isEmpty()) { 579 pos++; 580 nameCell(tr, context.formatPhrase(RenderingContext.PAT_ACTIVE), context.formatPhrase(RenderingContext.PAT_ACTIVE_HINT)); 581 XhtmlNode td = tr.td(); 582 if (pos == count) { 583 td.colspan("3"); 584 } 585 renderDataType(status, xlinkNarrative(td, a.get(0)), a.get(0)); 586 } 587 } 588 if (r.has("deceased[x]")) { 589 List<ResourceWrapper> a = r.children("deceased[x]"); 590 if (!a.isEmpty()) { 591 pos++; 592 nameCell(tr, context.formatPhrase(RenderingContext.PAT_DECEASED), context.formatPhrase(RenderingContext.PAT_DECEASED_HINT)); 593 XhtmlNode td = tr.td(); 594 if (pos == count) { 595 td.colspan("3"); 596 } 597 renderDataType(status, xlinkNarrative(td, a.get(0)), a.get(0)); 598 } 599 } 600 if (r.has("maritalStatus")) { 601 List<ResourceWrapper> a = r.children("maritalStatus"); 602 if (!a.isEmpty()) { 603 pos++; 604 if (pos == 3) { 605 tr = tbl.tr(); 606 } 607 nameCell(tr, context.formatPhrase(RenderingContext.PAT_MARITAL), context.formatPhrase(RenderingContext.PAT_MARITAL_HINT)); 608 XhtmlNode td = tr.td(); 609 if (pos == count) { 610 td.colspan("3"); 611 } 612 renderDataType(status, xlinkNarrative(td, a.get(0)), a.get(0)); 613 } 614 } 615 if (r.has("multipleBirth[x]")) { 616 List<ResourceWrapper> a = r.children("multipleBirth[x]"); 617 if (!a.isEmpty()) { 618 pos++; 619 if (pos == 3) { 620 tr = tbl.tr(); 621 } 622 nameCell(tr, context.formatPhrase(RenderingContext.PAT_MUL_BIRTH), context.formatPhrase(RenderingContext.PAT_MUL_BIRTH_HINT)); 623 XhtmlNode td = tr.td(); 624 if (pos == count) { 625 td.colspan("3"); 626 } 627 renderDataType(status, xlinkNarrative(td, a.get(0)), a.get(0)); 628 } 629 } 630 } 631 } 632 633 private void nameCell(XhtmlNode tr, String text, String title) { 634 XhtmlNode td = tr.td(); 635 td.setAttribute("title", title); 636 td.tx(text); 637 td.style("background-color: #f3f5da"); 638 markBoilerplate(td); 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 markBoilerplate(td); 651 } 652 653 private void renderPhoto(XhtmlNode td, ResourceWrapper r) throws UnsupportedEncodingException, FHIRException, IOException { 654 if (r.has("photo")) { 655 List<ResourceWrapper> a = r.children("photo"); 656 for (ResourceWrapper att : a) { 657 String ct = att.primitiveValue("contentType"); 658 byte[] cnt = att.has("data") ? Base64.getDecoder().decode(att.primitiveValue("data")) : null; 659 if (ct.startsWith("image/") && 660 cnt != null && (!context.isInlineGraphics() || (cnt.length > 0 && cnt.length < MAX_IMAGE_LENGTH))) { 661 String ext = extensionForType(ct); 662 if (context.isInlineGraphics() || Utilities.noString(context.getDestDir()) || ext == null) { 663 td.img("data:"+ct+";base64,"+att.primitiveValue("data"), "patient photo"); 664 } else { 665 String n = UUID.randomUUID().toString().toLowerCase()+ext; 666 FileUtilities.bytesToFile(cnt, ManagedFileAccess.file(Utilities.path(context.getDestDir(), n))); 667 context.registerFile(n); 668 td.img(n, context.formatPhrase(RenderingContext.PAT_PHOTO)); 669 } 670 return; 671 } 672 } 673 } 674 return; 675 } 676 677 private String extensionForType(String contentType) { 678 if (contentType.equals("image/gif")) { 679 return ".gif"; 680 } 681 if (contentType.equals("image/png")) { 682 return ".png"; 683 } 684 if (contentType.equals("image/jpeg")) { 685 return ".jpg"; 686 } 687 return null; 688 } 689 690 private boolean hasRenderablePhoto(ResourceWrapper r) throws UnsupportedEncodingException, FHIRException, IOException { 691 if (r.has("photo")) { 692 List<ResourceWrapper> a = r.children("photo"); 693 for (ResourceWrapper att : a) { 694 if (att.has("contentType") && att.primitiveValue("contentType").startsWith("image/") && 695 att.has("data") && (!context.isInlineGraphics() || (att.primitiveValue("data").length() > 0 && 696 att.primitiveValue("data").length() < MAX_IMAGE_LENGTH))) { 697 return true; 698 } 699 } 700 } 701 return false; 702 } 703 704 private XhtmlNode makeBanner(XhtmlNode para, ResourceWrapper res) { 705 para.style("border: 1px #661aff solid; background-color: #e6e6ff; padding: 10px;"); 706 xlinkNarrative(para, res); 707 return para; 708 } 709}