mirror of
https://gitlab.com/signalytic/client-external/streamline/streamline-emr.git
synced 2026-09-12 03:01:32 +00:00
The official image from streamline does not currently work for the arm64 platform. As a temporary measure, the source code and docker build scripts have been lifted from the official images and are used to build locally. Some additional modifications are made to reduce overall image size, these are documented in docker/README.md
241 lines
7.2 KiB
PHP
241 lines
7.2 KiB
PHP
<?php
|
|
|
|
namespace TijsVerkoyen\CssToInlineStyles;
|
|
|
|
use Symfony\Component\CssSelector\CssSelector;
|
|
use Symfony\Component\CssSelector\CssSelectorConverter;
|
|
use Symfony\Component\CssSelector\Exception\ExceptionInterface;
|
|
use TijsVerkoyen\CssToInlineStyles\Css\Processor;
|
|
use TijsVerkoyen\CssToInlineStyles\Css\Property\Processor as PropertyProcessor;
|
|
use TijsVerkoyen\CssToInlineStyles\Css\Rule\Processor as RuleProcessor;
|
|
|
|
class CssToInlineStyles
|
|
{
|
|
private $cssConverter;
|
|
|
|
public function __construct()
|
|
{
|
|
if (class_exists('Symfony\Component\CssSelector\CssSelectorConverter')) {
|
|
$this->cssConverter = new CssSelectorConverter();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Will inline the $css into the given $html
|
|
*
|
|
* Remark: if the html contains <style>-tags those will be used, the rules
|
|
* in $css will be appended.
|
|
*
|
|
* @param string $html
|
|
* @param string $css
|
|
*
|
|
* @return string
|
|
*/
|
|
public function convert($html, $css = null)
|
|
{
|
|
$document = $this->createDomDocumentFromHtml($html);
|
|
$processor = new Processor();
|
|
|
|
// get all styles from the style-tags
|
|
$rules = $processor->getRules(
|
|
$processor->getCssFromStyleTags($html)
|
|
);
|
|
|
|
if ($css !== null) {
|
|
$rules = $processor->getRules($css, $rules);
|
|
}
|
|
|
|
$document = $this->inline($document, $rules);
|
|
|
|
return $this->getHtmlFromDocument($document);
|
|
}
|
|
|
|
/**
|
|
* Inline the given properties on an given DOMElement
|
|
*
|
|
* @param \DOMElement $element
|
|
* @param Css\Property\Property[] $properties
|
|
*
|
|
* @return \DOMElement
|
|
*/
|
|
public function inlineCssOnElement(\DOMElement $element, array $properties)
|
|
{
|
|
if (empty($properties)) {
|
|
return $element;
|
|
}
|
|
|
|
$cssProperties = array();
|
|
$inlineProperties = array();
|
|
|
|
foreach ($this->getInlineStyles($element) as $property) {
|
|
$inlineProperties[$property->getName()] = $property;
|
|
}
|
|
|
|
foreach ($properties as $property) {
|
|
if (!isset($inlineProperties[$property->getName()])) {
|
|
$cssProperties[$property->getName()] = $property;
|
|
}
|
|
}
|
|
|
|
$rules = array();
|
|
foreach (array_merge($cssProperties, $inlineProperties) as $property) {
|
|
$rules[] = $property->toString();
|
|
}
|
|
$element->setAttribute('style', implode(' ', $rules));
|
|
|
|
return $element;
|
|
}
|
|
|
|
/**
|
|
* Get the current inline styles for a given DOMElement
|
|
*
|
|
* @param \DOMElement $element
|
|
*
|
|
* @return Css\Property\Property[]
|
|
*/
|
|
public function getInlineStyles(\DOMElement $element)
|
|
{
|
|
$processor = new PropertyProcessor();
|
|
|
|
return $processor->convertArrayToObjects(
|
|
$processor->splitIntoSeparateProperties(
|
|
$element->getAttribute('style')
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param string $html
|
|
*
|
|
* @return \DOMDocument
|
|
*/
|
|
protected function createDomDocumentFromHtml($html)
|
|
{
|
|
$document = new \DOMDocument('1.0', 'UTF-8');
|
|
$internalErrors = libxml_use_internal_errors(true);
|
|
$document->loadHTML(mb_encode_numericentity($html, [0x80, 0x10FFFF, 0, 0x1FFFFF], 'UTF-8'));
|
|
libxml_use_internal_errors($internalErrors);
|
|
$document->formatOutput = true;
|
|
|
|
return $document;
|
|
}
|
|
|
|
/**
|
|
* @param \DOMDocument $document
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function getHtmlFromDocument(\DOMDocument $document)
|
|
{
|
|
// retrieve the document element
|
|
// we do it this way to preserve the utf-8 encoding
|
|
$htmlElement = $document->documentElement;
|
|
$html = $document->saveHTML($htmlElement);
|
|
$html = trim($html);
|
|
|
|
// retrieve the doctype
|
|
$document->removeChild($htmlElement);
|
|
$doctype = $document->saveHTML();
|
|
$doctype = trim($doctype);
|
|
|
|
// if it is the html5 doctype convert it to lowercase
|
|
if ($doctype === '<!DOCTYPE html>') {
|
|
$doctype = strtolower($doctype);
|
|
}
|
|
|
|
return $doctype."\n".$html;
|
|
}
|
|
|
|
/**
|
|
* @param \DOMDocument $document
|
|
* @param Css\Rule\Rule[] $rules
|
|
*
|
|
* @return \DOMDocument
|
|
*/
|
|
protected function inline(\DOMDocument $document, array $rules)
|
|
{
|
|
if (empty($rules)) {
|
|
return $document;
|
|
}
|
|
|
|
$propertyStorage = new \SplObjectStorage();
|
|
|
|
$xPath = new \DOMXPath($document);
|
|
|
|
usort($rules, array(RuleProcessor::class, 'sortOnSpecificity'));
|
|
|
|
foreach ($rules as $rule) {
|
|
try {
|
|
if (null !== $this->cssConverter) {
|
|
$expression = $this->cssConverter->toXPath($rule->getSelector());
|
|
} else {
|
|
// Compatibility layer for Symfony 2.7 and older
|
|
$expression = CssSelector::toXPath($rule->getSelector());
|
|
}
|
|
} catch (ExceptionInterface $e) {
|
|
continue;
|
|
}
|
|
|
|
$elements = $xPath->query($expression);
|
|
|
|
if ($elements === false) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($elements as $element) {
|
|
$propertyStorage[$element] = $this->calculatePropertiesToBeApplied(
|
|
$rule->getProperties(),
|
|
$propertyStorage->contains($element) ? $propertyStorage[$element] : array()
|
|
);
|
|
}
|
|
}
|
|
|
|
foreach ($propertyStorage as $element) {
|
|
$this->inlineCssOnElement($element, $propertyStorage[$element]);
|
|
}
|
|
|
|
return $document;
|
|
}
|
|
|
|
/**
|
|
* Merge the CSS rules to determine the applied properties.
|
|
*
|
|
* @param Css\Property\Property[] $properties
|
|
* @param Css\Property\Property[] $cssProperties existing applied properties indexed by name
|
|
*
|
|
* @return Css\Property\Property[] updated properties, indexed by name
|
|
*/
|
|
private function calculatePropertiesToBeApplied(array $properties, array $cssProperties)
|
|
{
|
|
if (empty($properties)) {
|
|
return $cssProperties;
|
|
}
|
|
|
|
foreach ($properties as $property) {
|
|
if (isset($cssProperties[$property->getName()])) {
|
|
$existingProperty = $cssProperties[$property->getName()];
|
|
|
|
//skip check to overrule if existing property is important and current is not
|
|
if ($existingProperty->isImportant() && !$property->isImportant()) {
|
|
continue;
|
|
}
|
|
|
|
//overrule if current property is important and existing is not, else check specificity
|
|
$overrule = !$existingProperty->isImportant() && $property->isImportant();
|
|
if (!$overrule) {
|
|
$overrule = $existingProperty->getOriginalSpecificity()->compareTo($property->getOriginalSpecificity()) <= 0;
|
|
}
|
|
|
|
if ($overrule) {
|
|
unset($cssProperties[$property->getName()]);
|
|
$cssProperties[$property->getName()] = $property;
|
|
}
|
|
} else {
|
|
$cssProperties[$property->getName()] = $property;
|
|
}
|
|
}
|
|
|
|
return $cssProperties;
|
|
}
|
|
}
|