
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; 010 011import org.hl7.fhir.exceptions.DefinitionException; 012import org.hl7.fhir.exceptions.FHIRException; 013import org.hl7.fhir.exceptions.FHIRFormatError; 014import org.hl7.fhir.r5.model.StructureDefinition; 015import org.hl7.fhir.r5.renderers.utils.RenderingContext; 016import org.hl7.fhir.r5.renderers.utils.ResourceWrapper; 017import org.hl7.fhir.r5.utils.EOperationOutcome; 018import org.hl7.fhir.utilities.FileUtilities; 019import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage; 020import org.hl7.fhir.utilities.Utilities; 021import org.hl7.fhir.utilities.filesystem.ManagedFileAccess; 022import org.hl7.fhir.utilities.xhtml.XhtmlNode; 023 024@MarkedToMoveToAdjunctPackage 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, xlinkNarrative(x.b(), n), n); 123 } 124 x.tx(" "); 125 if (gender == null) { 126 x.tx(context.formatPhrase(RenderingContext.PAT_NO_GENDER)); 127 } else { 128 spanIfTracking(x, pat.child("gender")).tx(gender); 129 } 130 x.tx(", "); 131 if (dt == null) { 132 x.tx(context.formatPhrase(RenderingContext.PAT_NO_DOB)); 133 } else { 134 spanIfTracking(x, dt).tx(context.formatPhrase(RenderingContext.PAT_DOB, displayDateTime(dt))); 135 } 136 if (id != null) { 137 x.tx(" ( "); 138 renderDataType(status, spanIfTracking(x, id), id); 139 x.tx(")"); 140 } 141 } else { 142 // banner 143 makeBanner(x.para(), pat).tx(buildSummary(pat)); 144 x.hr(); 145 XhtmlNode tbl; 146 if (hasRenderablePhoto(pat)) { 147 tbl = x.table(null, true); 148 XhtmlNode tr = tbl.tr(); 149 tbl = tr.td().table("grid", false); 150 renderPhoto(tr.td(), pat); 151 } else { 152 tbl = x.table("grid", false); 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 private void addExtensions(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws UnsupportedEncodingException, FHIRException, IOException { 236 Map<String, List<ResourceWrapper>> extensions = new HashMap<>(); 237 List<ResourceWrapper> pw = r.children("extension"); 238 for (ResourceWrapper t : pw) { 239 String url = t.primitiveValue("url"); 240 if (!extensions.containsKey(url)) { 241 extensions.put(url, new ArrayList<>()); 242 } 243 extensions.get(url).add(t); 244 } 245 246 for (String url : extensions.keySet()) { 247 StructureDefinition sd = findCanonical(StructureDefinition.class, url, r); 248 if (sd != null) { 249 List<ResourceWrapper> list = extensions.get(url); 250 boolean anyComplex = false; 251 for (ResourceWrapper ext : list) { 252 anyComplex = anyComplex || ext.has("extension"); 253 } 254 if (!anyComplex) { 255 XhtmlNode tr = tbl.tr(); 256 nameCell(tr, getContext().getTranslated(sd.getTitleElement()), sd.getDescription(), sd.getWebPath()); 257 XhtmlNode td = tr.td(); 258 td.colspan("3"); 259 if (list.size() != 1) { 260 XhtmlNode ul = td.ul(); 261 for (ResourceWrapper s : list) { 262 XhtmlNode li = ul.li(); 263 renderDataType(status, xlinkNarrative(li, s.child("value")), s.child("value")); 264 } 265 } else { 266 renderDataType(status, xlinkNarrative(td, list.get(0).child("value")), list.get(0).child("value")); 267 } 268 } else { 269 for (ResourceWrapper ext : list) { 270 XhtmlNode tr = tbl.tr(); 271 nameCell(tr, sd.getTitle()+":", sd.getDescription()); 272 XhtmlNode td = tr.td(); 273 td.colspan("3"); 274 if (ext.has("extension")) { 275 XhtmlNode ul = td.ul(); 276 for (ResourceWrapper s : ext.extensions()) { 277 XhtmlNode li = ul.li(); 278 li.tx(s.primitiveValue("url")+": "); 279 if (s.has("extension")) { 280 boolean first = true; 281 for (ResourceWrapper t : s.extensions()) { 282 if (first) first = false; else li.tx("; "); 283 li.tx(t.primitiveValue("url")+"="); 284 renderDataType(status, xlinkNarrative(li, t.child("value")), t.child("value")); 285 } 286 } else { 287 renderDataType(status, xlinkNarrative(li, s.child("value")), s.child("value")); 288 } 289 } 290 } else { 291 renderDataType(status, xlinkNarrative(td, ext.child("value")), ext.child("value")); 292 } 293 } 294 } 295 } 296 } 297 298 299 } 300 301 private void addIdentifiers(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException { 302 List<ResourceWrapper> ids = r.children("identifier"); 303 ResourceWrapper id = null; 304 for (ResourceWrapper i : ids) { 305 id = chooseId(id, i); 306 } 307 if (id != null) { 308 ids.remove(id); 309 }; 310 if (ids.size() > 0) { 311 XhtmlNode tr = tbl.tr(); 312 nameCell(tr, context.formatMessagePlural(ids.size(), RenderingContext.PAT_OTHER_ID),context.formatMessagePlural(ids.size(), RenderingContext.PAT_OTHER_ID_HINT)); 313 XhtmlNode td = tr.td(); 314 td.colspan("3"); 315 if (ids.size() == 1) { 316 renderDataType(status, xlinkNarrative(td, ids.get(0)), ids.get(0)); 317 } else { 318 XhtmlNode ul = td.ul(); 319 for (ResourceWrapper i : ids) { 320 renderDataType(status, xlinkNarrative(ul.li(), i), i); 321 } 322 } 323 } 324 } 325 326 private void addLangs(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException { 327 List<ResourceWrapper> langs = new ArrayList<ResourceWrapper>(); 328 List<ResourceWrapper> comms = r.children("communication"); 329 ResourceWrapper prefLang = null; 330 for (ResourceWrapper t : comms) { 331 ResourceWrapper lang = t.child("language"); 332 if (lang != null) { 333 langs.add(lang); 334 ResourceWrapper l = t.child("preferred"); 335 if (l != null && "true".equals(l.primitiveValue())) { 336 prefLang = lang; 337 } 338 } 339 } 340 if (langs.size() > 0) { 341 XhtmlNode tr = tbl.tr(); 342 nameCell(tr, context.formatMessagePlural(langs.size(), RenderingContext.PAT_LANG), context.formatMessagePlural(langs.size(), RenderingContext.PAT_LANG_HINT)); 343 XhtmlNode td = tr.td(); 344 td.colspan("3"); 345 if (langs.size() == 1) { 346 renderDataType(status, xlinkNarrative(td, langs.get(0)), langs.get(0)); 347 if (prefLang != null) { 348 td.tx(" "+context.formatPhrase(RenderingContext.PAT_LANG_PREFERRED)); 349 } 350 } else if (langs.size() > 1) { 351 XhtmlNode ul = td.ul(); 352 for (ResourceWrapper i : langs) { 353 XhtmlNode li = ul.li(); 354 renderDataType(status, xlinkNarrative(li, i), i); 355 if (i == prefLang) { 356 li.tx(" "+context.formatPhrase(RenderingContext.PAT_LANG_PREFERRED));; 357 } 358 } 359 } 360 } 361 } 362 363 364 365 public class NamedReferance { 366 367 private String name; 368 private ResourceWrapper type; 369 private ResourceWrapper reference; 370 371 public NamedReferance(String name, ResourceWrapper type, ResourceWrapper ref) { 372 this.name = name; 373 this.type = type; 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 public ResourceWrapper getType() { 386 return type; 387 } 388 389 } 390 391 392 private void addLinks(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws UnsupportedEncodingException, FHIRException, IOException { 393 List<NamedReferance> refs = new ArrayList<>(); 394 List<ResourceWrapper> pw = r.children("generalPractitioner"); 395 for (ResourceWrapper t : pw) { 396 refs.add(new NamedReferance(context.formatPhrase(RenderingContext.PAT_GP), null, t)); 397 } 398 pw = r.children("managingOrganization"); 399 for (ResourceWrapper t : pw) { 400 refs.add(new NamedReferance(context.formatPhrase(RenderingContext.PAT_MO), null, t)); 401 } 402 pw = r.children("link"); 403 for (ResourceWrapper t : pw) { 404 ResourceWrapper o = t.firstChild("other"); 405 ResourceWrapper l = t.firstChild("type"); 406 if (l != null && o != null) { 407 refs.add(new NamedReferance(describeLinkedRecord(l.primitiveValue()), l, o)); 408 } 409 } 410 411 if (refs.size() > 0) { 412 XhtmlNode tr = tbl.tr(); 413 nameCell(tr, context.formatPhrase(RenderingContext.PAT_LINKS), context.formatPhrase(RenderingContext.PAT_LINKS_HINT)); 414 XhtmlNode td = tr.td(); 415 td.colspan("3"); 416 XhtmlNode ul = td.ul(); 417 for (NamedReferance ref : refs) { 418 XhtmlNode li = ul.li(); 419 if (ref.getType() != null) { 420 spanIfTracking(li, ref.getType()).tx(ref.getName()); 421 } else { 422 li.tx(ref.getName()); 423 } 424 li.tx(": "); 425 renderReference(status, li, ref.getReference()); 426 } 427 } 428 } 429 430 private String describeLinkedRecord(String type) { 431 switch (type) { 432 case "replaced-by" : return context.formatPhrase(RenderingContext.PAT_LINK_REPLBY); 433 case "replaces": return context.formatPhrase(RenderingContext.PAT_LINK_REPL); 434 case "refer": return context.formatPhrase(RenderingContext.PAT_LINK_REFER); 435 case "seealso": return context.formatPhrase(RenderingContext.PAT_LINK_SEE); 436 } 437 return "Unknown"; 438 } 439 440 private void addNOKs(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException { 441 for (ResourceWrapper t : r.children("contact")) { 442 addNOK(status, tbl, r, t); 443 } 444 } 445 446 private void addNOK(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r, ResourceWrapper bw) throws FHIRFormatError, DefinitionException, IOException { 447 List<ResourceWrapper> rels = bw.children("relationship"); 448 ResourceWrapper name = bw.firstChild("name"); 449 ResourceWrapper add = bw.firstChild("address"); 450 String gender = context.getTranslatedCode(bw.primitiveValue("gender"), "http://hl7.org/fhir/administrative-gender"); 451 ResourceWrapper period = bw.firstChild("period"); 452 ResourceWrapper organization = bw.firstChild("organization"); 453 List<ResourceWrapper> tels = bw.children("telecom"); 454 455 if (rels.size() < 2 && name == null && add == null && gender == null && period == null && organization == null && tels.size() == 0) { 456 return; // nothing to render 457 } 458 XhtmlNode tr = tbl.tr(); 459 if (rels.size() == 1) { 460 nameCell(tr, displayDataType(rels.get(0))+":", context.formatPhrase(RenderingContext.PAT_NOM_CONTACT)+" "+displayDataType(rels.get(0))); 461 } else { 462 nameCell(tr, context.formatPhrase(RenderingContext.GENERAL_CONTACT), context.formatPhrase(RenderingContext.PAT_NOK_CONTACT_HINT)); 463 } 464 XhtmlNode td = tr.td(); 465 td.colspan("3"); 466 XhtmlNode ul = td.ul(); 467 XhtmlNode li; 468 if (name != null) { 469 li = ul.li(); 470 renderDataType(status, xlinkNarrative(li, name), name); 471 if (gender != null) { 472 li.tx(" "+"("+gender+")"); 473 } 474 } else if (gender != null) { 475 li = ul.li(); 476 li.tx(context.formatPhrase(RenderingContext.PAT_GENDER, gender)); 477 } 478 if (rels.size() > 1) { 479 li = ul.li(); 480 li.tx(context.formatPhrase(RenderingContext.PAT_RELN)); 481 boolean first = true; 482 for (ResourceWrapper rel : rels) { 483 if (first) first = false; else li.tx(", "); 484 renderDataType(status, xlinkNarrative(li, rel), rel); 485 } 486 } 487 if (add != null) { 488 renderDataType(status, xlinkNarrative(ul.li(), add), add); 489 } 490 for (ResourceWrapper cp : tels) { 491 renderDataType(status, xlinkNarrative(ul.li(), cp), cp); 492 } 493 if (organization != null) { 494 li = ul.li(); 495 li.tx(context.formatPhrase(RenderingContext.PAT_ORG)); 496 renderDataType(status, xlinkNarrative(li, organization), organization); 497 } 498 if (period != null) { 499 li = ul.li(); 500 li.tx(context.formatPhrase(RenderingContext.PAT_PERIOD)); 501 renderDataType(status, xlinkNarrative(li, period), period); 502 } 503 } 504 505 private void addNames(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException { 506 List<ResourceWrapper> names = r.children("name"); 507 ResourceWrapper name = null; 508 for (ResourceWrapper n : names) { 509 name = chooseName(name, n); 510 } 511 if (name != null) { 512 names.remove(name); 513 }; 514 if (names.size() == 1) { 515 XhtmlNode tr = tbl.tr(); 516 nameCell(tr, context.formatPhrase(RenderingContext.PAT_ALT_NAME), context.formatPhrase(RenderingContext.PAT_ALT_NAME_HINT)); 517 XhtmlNode td = tr.td(); 518 td.colspan("3"); 519 if (names.size() == 1) { 520 renderDataType(status, xlinkNarrative(td, names.get(0)), names.get(0)); 521 } else { 522 XhtmlNode ul = td.ul(); 523 for (ResourceWrapper n : names) { 524 renderDataType(status,xlinkNarrative(ul.li(), n), n); 525 } 526 } 527 } 528 } 529 530 private void addComms(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException { 531 List<ResourceWrapper> tels = r.children("telecom"); 532 List<ResourceWrapper> adds = r.children("address"); 533 if (tels.size() + adds.size() > 0) { 534 XhtmlNode tr = tbl.tr(); 535 nameCell(tr, context.formatPhrase(RenderingContext.PAT_CONTACT), context.formatPhrase(RenderingContext.PAT_CONTACT_HINT)); 536 XhtmlNode td = tr.td(); 537 td.colspan("3"); 538 if (tels.size() + adds.size() == 1) { 539 if (adds.isEmpty()) { 540 renderDataType(status, xlinkNarrative(td, tels.get(0)), tels.get(0)); 541 } else { 542 renderDataType(status, xlinkNarrative(td, adds.get(0)), adds.get(0)); 543 } 544 } else { 545 XhtmlNode ul = td.ul(); 546 for (ResourceWrapper n : tels) { 547 renderDataType(status, xlinkNarrative(ul.li(), n), n); 548 } 549 for (ResourceWrapper n : adds) { 550 renderDataType(status, xlinkNarrative(ul.li(), n), n); 551 } 552 } 553 } 554 } 555 556 private void addStatus(RenderingStatus status, XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, UnsupportedEncodingException, FHIRException, IOException { 557 // TODO Auto-generated method stub 558 int count = 0; 559 if (r.has("active")) { 560 count++; 561 } 562 if (r.has("deceased")) { 563 count++; 564 } 565 if (r.has("maritalStatus")) { 566 count++; 567 } 568 if (r.has("multipleBirth")) { 569 count++; 570 } 571 if (count > 0) { 572 XhtmlNode tr = tbl.tr(); 573 int pos = 0; 574 if (r.has("active")) { 575 List<ResourceWrapper> a = r.children("active"); 576 if (!a.isEmpty()) { 577 pos++; 578 nameCell(tr, context.formatPhrase(RenderingContext.PAT_ACTIVE), context.formatPhrase(RenderingContext.PAT_ACTIVE_HINT)); 579 XhtmlNode td = tr.td(); 580 if (pos == count) { 581 td.colspan("3"); 582 } 583 renderDataType(status, xlinkNarrative(td, a.get(0)), a.get(0)); 584 } 585 } 586 if (r.has("deceased[x]")) { 587 List<ResourceWrapper> a = r.children("deceased[x]"); 588 if (!a.isEmpty()) { 589 pos++; 590 nameCell(tr, context.formatPhrase(RenderingContext.PAT_DECEASED), context.formatPhrase(RenderingContext.PAT_DECEASED_HINT)); 591 XhtmlNode td = tr.td(); 592 if (pos == count) { 593 td.colspan("3"); 594 } 595 renderDataType(status, xlinkNarrative(td, a.get(0)), a.get(0)); 596 } 597 } 598 if (r.has("maritalStatus")) { 599 List<ResourceWrapper> a = r.children("maritalStatus"); 600 if (!a.isEmpty()) { 601 pos++; 602 if (pos == 3) { 603 tr = tbl.tr(); 604 } 605 nameCell(tr, context.formatPhrase(RenderingContext.PAT_MARITAL), context.formatPhrase(RenderingContext.PAT_MARITAL_HINT)); 606 XhtmlNode td = tr.td(); 607 if (pos == count) { 608 td.colspan("3"); 609 } 610 renderDataType(status, xlinkNarrative(td, a.get(0)), a.get(0)); 611 } 612 } 613 if (r.has("multipleBirth[x]")) { 614 List<ResourceWrapper> a = r.children("multipleBirth[x]"); 615 if (!a.isEmpty()) { 616 pos++; 617 if (pos == 3) { 618 tr = tbl.tr(); 619 } 620 nameCell(tr, context.formatPhrase(RenderingContext.PAT_MUL_BIRTH), context.formatPhrase(RenderingContext.PAT_MUL_BIRTH_HINT)); 621 XhtmlNode td = tr.td(); 622 if (pos == count) { 623 td.colspan("3"); 624 } 625 renderDataType(status, xlinkNarrative(td, a.get(0)), a.get(0)); 626 } 627 } 628 } 629 } 630 631 private void nameCell(XhtmlNode tr, String text, String title) { 632 XhtmlNode td = tr.td(); 633 td.setAttribute("title", title); 634 td.tx(text); 635 td.style("background-color: #f3f5da"); 636 markBoilerplate(td); 637 } 638 639 private void nameCell(XhtmlNode tr, String text, String title, String link) { 640 XhtmlNode td = tr.td(); 641 td.setAttribute("title", title); 642 if (link != null) { 643 td.ah(context.prefixLocalHref(link)).tx(text); 644 } else { 645 td.tx(text); 646 } 647 td.style("background-color: #f3f5da"); 648 markBoilerplate(td); 649 } 650 651 private void renderPhoto(XhtmlNode td, ResourceWrapper r) throws UnsupportedEncodingException, FHIRException, IOException { 652 if (r.has("photo")) { 653 List<ResourceWrapper> a = r.children("photo"); 654 for (ResourceWrapper att : a) { 655 String ct = att.primitiveValue("contentType"); 656 byte[] cnt = att.has("data") ? Base64.getDecoder().decode(att.primitiveValue("data")) : null; 657 if (ct.startsWith("image/") && 658 cnt != null && (!context.isInlineGraphics() || (cnt.length > 0 && cnt.length < MAX_IMAGE_LENGTH))) { 659 String ext = extensionForType(ct); 660 if (context.isInlineGraphics() || Utilities.noString(context.getDestDir()) || ext == null) { 661 td.img("data:"+ct+";base64,"+att.primitiveValue("data"), "patient photo"); 662 } else { 663 String n = context.getRandomName(r.getId())+ext; 664 FileUtilities.bytesToFile(cnt, ManagedFileAccess.file(Utilities.path(context.getDestDir(), n))); 665 context.registerFile(n); 666 td.img(n, context.formatPhrase(RenderingContext.PAT_PHOTO)); 667 } 668 return; 669 } 670 } 671 } 672 return; 673 } 674 675 private String extensionForType(String contentType) { 676 if (contentType.equals("image/gif")) { 677 return ".gif"; 678 } 679 if (contentType.equals("image/png")) { 680 return ".png"; 681 } 682 if (contentType.equals("image/jpeg")) { 683 return ".jpg"; 684 } 685 return null; 686 } 687 688 private boolean hasRenderablePhoto(ResourceWrapper r) throws UnsupportedEncodingException, FHIRException, IOException { 689 if (r.has("photo")) { 690 List<ResourceWrapper> a = r.children("photo"); 691 for (ResourceWrapper att : a) { 692 if (att.has("contentType") && att.primitiveValue("contentType").startsWith("image/") && 693 att.has("data") && (!context.isInlineGraphics() || (att.primitiveValue("data").length() > 0 && 694 att.primitiveValue("data").length() < MAX_IMAGE_LENGTH))) { 695 return true; 696 } 697 } 698 } 699 return false; 700 } 701 702 private XhtmlNode makeBanner(XhtmlNode para, ResourceWrapper res) { 703 para.style("border: 1px #661aff solid; background-color: #e6e6ff; padding: 10px;"); 704 xlinkNarrative(para, res); 705 return para; 706 } 707}