'',
'attrs' => [],
'inlineAttrs' => []
];
foreach ($content['ops'] as $op) {
$text = '';
$attrs = isset($op['attributes']) ? $op['attributes'] : [];
// Gérer l'insertion de texte
if (is_string($op['insert'])) {
$text = $op['insert'];
// Si c'est un saut de ligne, on finalise le bloc
if ($text === "\n") {
// Vérifier les attributs de bloc pour ce saut de ligne
foreach ($attrs as $key => $value) {
if (in_array($key, self::$blockAttrs)) {
$currentBlock['attrs'][$key] = $value;
}
}
// Finaliser le bloc courant
if (!empty($currentBlock['content']) || !empty($currentBlock['attrs'])) {
$html .= self::renderBlock($currentBlock);
}
// Réinitialiser pour le prochain bloc
$currentBlock = [
'content' => '',
'attrs' => [],
'inlineAttrs' => []
];
} else {
// Ajouter le texte avec ses attributs inline
$currentBlock['content'] .= $text;
if (!empty($attrs)) {
$currentBlock['inlineAttrs'][] = [
'start' => mb_strlen($currentBlock['content']) - mb_strlen($text),
'length' => mb_strlen($text),
'attrs' => $attrs
];
}
}
}
// Gérer les images
elseif (is_array($op['insert']) && isset($op['insert']['image'])) {
if (!empty($currentBlock['content'])) {
$html .= self::renderBlock($currentBlock);
$currentBlock = [
'content' => '',
'attrs' => [],
'inlineAttrs' => []
];
}
$imageUrl = self::cleanImageUrl($op['insert']['image']);
$imgAttributes = ['class' => 'chapter-image'];
if (isset($attrs['alt'])) {
$imgAttributes['alt'] = $attrs['alt'];
} else {
$imgAttributes['alt'] = "Image";
}
$html .= self::createImageTag($imageUrl, $imgAttributes);
}
}
// Finaliser le dernier bloc si nécessaire
if (!empty($currentBlock['content'])) {
$html .= self::renderBlock($currentBlock);
}
return $html;
}
private static function renderBlock($block) {
if (empty($block['content'])) return '';
$tag = 'p';
$styles = [];
$classes = [];
// Déterminer le tag et attributs de base
if (!empty($block['attrs']['header'])) {
$tag = 'h' . $block['attrs']['header'];
} elseif (!empty($block['attrs']['blockquote'])) {
$tag = 'blockquote';
} elseif (!empty($block['attrs']['code-block'])) {
$tag = 'pre';
}
// Appliquer l'alignement au niveau du bloc
if (!empty($block['attrs']['align'])) {
$styles[] = "text-align: " . $block['attrs']['align'] . " !important";
}
// Appliquer la police au niveau du bloc si présente
if (!empty($block['attrs']['font'])) {
$classes[] = 'font-' . strtolower($block['attrs']['font']);
}
// Construire le contenu avec les attributs inline
$content = $block['content'];
if (!empty($block['inlineAttrs'])) {
// Trier les attributs par position de fin pour les appliquer de la fin vers le début
usort($block['inlineAttrs'], function($a, $b) {
return ($a['start'] + $a['length']) - ($b['start'] + $b['length']);
});
// Appliquer les attributs inline
foreach (array_reverse($block['inlineAttrs']) as $attr) {
$start = $attr['start'];
$length = $attr['length'];
$segment = mb_substr($content, $start, $length);
if (!empty($attr['attrs'])) {
$segment = self::applyInlineAttributes($segment, $attr['attrs']);
}
$content = mb_substr($content, 0, $start) . $segment . mb_substr($content, $start + $length);
}
}
// Si c'est un bloc de code, wrapper dans code
if ($tag === 'pre') {
$content = "$content
";
}
// Construire les attributs HTML finaux
$attributes = '';
if (!empty($styles)) {
$attributes .= ' style="' . implode('; ', $styles) . '"';
}
if (!empty($classes)) {
$attributes .= ' class="' . implode(' ', $classes) . '"';
}
return "<$tag$attributes>$content$tag>";
}
private static function applyInlineAttributes($text, $attrs) {
$text = htmlspecialchars($text);
if (!empty($attrs['bold'])) {
$text = "$text";
}
if (!empty($attrs['italic'])) {
$text = "$text";
}
if (!empty($attrs['underline'])) {
$text = "$text";
}
if (!empty($attrs['color'])) {
$text = "$text";
}
if (!empty($attrs['background'])) {
$text = "$text";
}
if (!empty($attrs['link'])) {
$text = "$text";
}
if (!empty($attrs['font'])) {
$text = "$text";
}
if (!empty($attrs['script'])) {
if ($attrs['script'] === 'super') $text = "$text";
if ($attrs['script'] === 'sub') $text = "$text";
}
return $text;
}
private static function isJson($string) {
json_decode($string);
return json_last_error() === JSON_ERROR_NONE;
}
private static function cleanImageUrl($url) {
return preg_replace('/^(?:\.\.\/)+/', '', $url);
}
private static function cleanImageUrls($html) {
return preg_replace_callback(
'/
]+src=([\'"])((?:\.\.\/)*(?:assets\/[^"\']+))\1[^>]*>/',
function($matches) {
$cleanUrl = self::cleanImageUrl($matches[2]);
return str_replace($matches[2], $cleanUrl, $matches[0]);
},
$html
);
}
private static function createImageTag($src, $attributes) {
$html = '
$value) {
$html .= ' ' . $key . '="' . htmlspecialchars($value) . '"';
}
$html .= '>';
return $html;
}
}