<?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;
}
}
Service Section
Discover how our services are designed to enhance your NFC Pay experience with convenience, security, and innovative solutions. From managing transactions to secure payments, we are dedicated to providing seamless support every step of the way.
Easily save your credit and debit card details within our app for quick and secure transactions. This feature ensures that your payment information is protected with advanced encryption and can be used for future purchases with just a tap.
Transfer funds quickly and securely between users with our streamlined money transfer service. Simply select the recipient, enter the amount, and authorize the transaction for instant, hassle-free transfers.
Activate your merchant account effortlessly to start receiving payments. Our intuitive setup process ensures that you can begin accepting transactions smoothly, helping your business thrive with minimal setup time.
Keep track of all your transactions in real time through our app. Monitor payment statuses, view transaction history, and manage your account efficiently, ensuring complete control over your financial activities.
Our dedicated support team is available to assist you with any queries or issues. Whether you need help with setting up your account or resolving transaction-related questions, we’re here to provide prompt and reliable assistance.