Build modified streamline images

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
This commit is contained in:
2024-03-10 16:17:50 -07:00
parent 2ffc3c408a
commit a424394109
13506 changed files with 1860172 additions and 6 deletions
@@ -0,0 +1,240 @@
<?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;
}
}