') === 0) { $parrafo = parsea_elementos_en_linea($parrafo); $resultado .= parsea_cita($parrafo); } elseif (strpos($parrafo, '- ') === 0 || strpos($parrafo, '* ') === 0 || preg_match('/^\d+\.\s/', $parrafo)) { $parrafo = parsea_elementos_en_linea($parrafo); $resultado .= parsea_listas($parrafo); } elseif (preg_match('/\n:?---:? \| :?---:?/', $parrafo)) { $parrafo = parsea_elementos_en_linea($parrafo); $resultado .= parsea_tabla($parrafo); } elseif (preg_match('/^#{1,6}\s.+/', $parrafo)) { $parrafo = parsea_elementos_en_linea($parrafo); $resultado .= parsea_titulo($parrafo); } elseif (preg_match('/^(```|~~~)/', $parrafo) && preg_match('/(```|~~~)$/', $parrafo)) { //bloque de código, no se parsean elementos en línea $resultado .= parsea_codigo($parrafo); } // Párrafo normal else { $parrafo = parsea_elementos_en_linea($parrafo); $resultado .= '

' . $parrafo . '

' . "\n"; } } return $resultado; } function parsea_cita($texto) { // Contar cuántos "> " hay al principio $nivel = 0; $html = ""; // Eliminar "> " nivel por nivel while (strpos($texto, '> ') === 0) { $nivel++; $texto = substr($texto, 2); } // Añadir los blockquote de apertura for ($i = 0; $i < $nivel; $i++) { $html .= '
'; } // Añadir el contenido $html .= $texto; // Añadir los blockquote de cierre for ($i = 0; $i < $nivel; $i++) { $html .= '
'; } $html .= "\n"; return $html; } function parsea_listas($texto) { // Dividir el texto en líneas $lineas = explode("\n", $texto); $total_lineas = count($lineas); // Determinar el tipo de lista principal según la primera línea (sin espacios iniciales) $primer_elemento = ltrim($lineas[0]); $tipo_lista = ''; $tipo_lista_hija = ''; if (strpos($primer_elemento, '- ') === 0 || strpos($primer_elemento, '* ') === 0) { $tipo_lista = 'ul'; } elseif (preg_match('/^\d+\.\s/', $primer_elemento)) { $tipo_lista = 'ol'; } $i = 0; while ($i < $total_lineas) { $linea = $lineas[$i]; $linea = rtrim($linea); if ($linea === '') { $i++; continue; } // Detectar si tiene dos espacios o tabulador al inicio (sublista) if (preg_match('/^( |\t)/', $linea)) { $linea_sin_indent = preg_replace('/^( |\t)/', '', $linea); $linea_sin_espacios_inicio = ltrim($linea_sin_indent); if (strpos($linea_sin_espacios_inicio, '- ') === 0) { $contenido = substr($linea_sin_espacios_inicio, 2); $marcador = '-'; } elseif (strpos($linea_sin_espacios_inicio, '* ') === 0) { $contenido = substr($linea_sin_espacios_inicio, 2); $marcador = '*'; } elseif (preg_match('/^\d+\.\s/', $linea_sin_espacios_inicio)) { $contenido = preg_replace('/^\d+\.\s/', '', $linea_sin_espacios_inicio); $marcador = 'numero'; } else { $contenido = $linea_sin_indent; $marcador = ''; } if ($marcador !== '') { $j = $i - 1; while ($j >= 0 && trim($lineas[$j]) === '') { $j--; } if ($j >= 0) { if ($tipo_lista_hija === '') { if ($marcador === 'numero') { $tipo_lista_hija = 'ol'; } else { $tipo_lista_hija = 'ul'; } $lineas[$j] .= '<' . $tipo_lista_hija . '>'; } $lineas[$j] .= '
  • ' . $contenido; $lineas[$i] = ''; } } } else { $tipo_lista_hija = ''; $linea_sin_espacios_inicio = ltrim($linea); if (strpos($linea_sin_espacios_inicio, '- ') === 0) { $contenido = substr($linea_sin_espacios_inicio, 2); $lineas[$i] = '
  • ' . $contenido; } elseif (strpos($linea_sin_espacios_inicio, '* ') === 0) { $contenido = substr($linea_sin_espacios_inicio, 2); $lineas[$i] = '
  • ' . $contenido; } elseif (preg_match('/^\d+\.\s/', $linea_sin_espacios_inicio)) { $contenido = preg_replace('/^\d+\.\s/', '', $linea_sin_espacios_inicio); $lineas[$i] = '
  • ' . $contenido; } else { $lineas[$i] = $linea; } } $i++; } // Filtrar líneas vacías $lineas_filtradas = array(); foreach ($lineas as $linea) { if (trim($linea) !== '') { $lineas_filtradas[] = $linea; } } // Segunda pasada: cerrar las listas hijas al final de cada línea foreach ($lineas_filtradas as $indice => $linea) { // Verificar si tiene '; } if (strpos($linea, '
      ') !== false && strpos($linea, '
    ') === false) { $lineas_filtradas[$indice] .= ''; } } //Tercera pasada: añadir los
  • foreach ($lineas_filtradas as $indice => $linea) { $linea = str_replace('
  • ', '
  • ', $linea); if (strpos($linea, '
  • ') === 0) { $linea = substr($linea, 5); } $linea = str_replace('
      ', '
        ', $linea); $linea = str_replace('
      ', '
    ', $linea); $linea = str_replace('', '', $linea); $linea .= ''; $lineas_filtradas[$indice] = $linea; } $resultado = ''; if ($tipo_lista !== '') { $resultado .= '<' . $tipo_lista . '>' . "\n"; } foreach ($lineas_filtradas as $linea) { $resultado .= $linea . "\n"; } if ($tipo_lista !== '') { $resultado .= '' . "\n"; } return $resultado; } function parsea_tabla($texto) { $lineas = explode("\n", $texto); $datos = []; // Primero parseamos las líneas en un array bidimensional foreach ($lineas as $linea) { $columnas = explode('|', $linea); $fila = []; foreach ($columnas as $columna) { $columna_limpia = trim($columna); $fila[] = $columna_limpia; } $datos[] = $fila; } // Verificamos que tenemos al menos encabezado y línea de separación if (count($datos) < 2) { return ""; } // Iniciamos la construcción del HTML de la tabla $html = "\n\n\n"; // Encabezados (primera fila) foreach ($datos[0] as $c => $valor) { // Determinamos la alineación basándonos en la segunda fila ($datos[1]) $formato = $datos[1][$c] ?? ''; $alineacion = ''; // "---" es igual a ":---" (alineación izquierda) if ($formato === '---') { $formato = ':---'; } if (preg_match('/^:.*:$/', $formato)) { $alineacion = ' style="text-align: center;"'; } elseif (strpos($formato, ':') === 0) { $alineacion = ' style="text-align: left;"'; } elseif (substr($formato, -1) === ':') { $alineacion = ' style="text-align: right;"'; } $html .= "" . $valor . "\n"; } $html .= "\n\n\n"; // Filas de datos (a partir de la tercera fila) for ($i = 2; $i < count($datos); $i++) { $html .= "\n"; foreach ($datos[$i] as $c => $valor) { // Determinamos la alineación basándonos en la segunda fila ($datos[1]) $formato = $datos[1][$c] ?? ''; $alineacion = ''; // "---" es igual a ":---" (alineación izquierda) if ($formato === '---') { $formato = ':---'; } if (preg_match('/^:.*:$/', $formato)) { $alineacion = ' style="text-align: center;"'; } elseif (strpos($formato, ':') === 0) { $alineacion = ' style="text-align: left;"'; } elseif (substr($formato, -1) === ':') { $alineacion = ' style="text-align: right;"'; } $html .= "" . $valor . "\n"; } $html .= "\n"; } $html .= "\n
    \n"; return $html; } function parsea_titulo($texto) { $nivel = 0; while ($nivel < strlen($texto) && $texto[$nivel] == '#') { $nivel++; if ($nivel >= 6) break; } $textoTitulo = trim(substr($texto, $nivel)); $cierre = str_repeat('#', $nivel); if (substr($textoTitulo, -strlen($cierre)) === $cierre) { $textoTitulo = substr($textoTitulo, 0, -strlen($cierre)); } $textoTitulo = trim($textoTitulo); return "$textoTitulo\n"; } function parsea_codigo($texto) { $lineas = explode("\n", $texto); array_shift($lineas); array_pop($lineas); $contenido_codigo = implode("\n", $lineas); return "
    " . $contenido_codigo . "
    \n"; } function preprocesar_bloques_codigo($texto) { // Buscar bloques de código y reemplazar \n\n por un marcador temporal return preg_replace_callback( '/(^|\n)(```|~~~)(?:\n.*?\n)\2(\n|$)/s', function($matches) { // Reemplazar \n\n por \n \n (espacio entre ellos) return str_replace("\n\n", "\n \n", $matches[0]); }, $texto ); } function sanear_listas($texto) { $lineas = explode("\n", $texto); $resultado = []; $en_lista = false; foreach ($lineas as $linea) { $es_item = preg_match('/^(\d+\.\s)/', $linea); $es_sangria = preg_match('/^(\s{2}|\t)/', $linea); $es_vacia = (trim($linea) === ''); if ($es_item) { // Si ya estamos en una lista, eliminar líneas vacías anteriores if ($en_lista) { while (!empty($resultado) && trim(end($resultado)) === '') { array_pop($resultado); } } $resultado[] = $linea; $en_lista = true; } // Línea con sangría cuando estamos en lista else if ($es_sangria && $en_lista) { // Eliminar líneas vacías anteriores while (!empty($resultado) && trim(end($resultado)) === '') { array_pop($resultado); } $resultado[] = $linea; } // Cualquier otra línea else { $resultado[] = $linea; // Si encontramos contenido que no es ítem ni línea con sangría, salimos del modo lista if (!$es_vacia) { $en_lista = false; } } } return implode("\n", $resultado); } function parsea_elementos_en_linea($texto) { $lineas = explode("\n", $texto); $almacen_enlaces = []; $almacen_imagenes = []; $almacen_codigos = []; foreach ($lineas as &$linea) { $linea = parsea_codigo_linea($linea, $almacen_codigos); $linea = parsea_imagenes($linea, $almacen_imagenes); $linea = parsea_enlaces($linea, $almacen_enlaces); $linea = parsea_nueva_linea($linea); $linea = escapa_caracteres($linea); $linea = parsea_negrita_cursiva($linea); $linea = parsea_seccion($linea); $linea = recoloca_elementos($linea, $almacen_codigos, $almacen_enlaces, $almacen_imagenes); } unset($linea); return implode("\n", $lineas); } function parsea_codigo_linea($linea, &$almacen_codigos) { $resultado = ''; $longitud = strlen($linea); for ($i = 0; $i < $longitud; $i++) { if ($linea[$i] === '`') { $inicio = $i; // Contar backticks del delimitador $num_backticks = 0; while ($i < $longitud && $linea[$i] === '`') { $num_backticks++; $i++; } // Buscar el cierre $contenido = ''; $cierre_encontrado = false; while ($i < $longitud) { if ($linea[$i] === '`') { // Contar backticks potenciales de cierre $temp_i = $i; $backticks_cierre = 0; while ($temp_i < $longitud && $linea[$temp_i] === '`') { $backticks_cierre++; $temp_i++; } // Si coincide con el número de apertura y no es parte de secuencia más larga if ($backticks_cierre === $num_backticks && ($temp_i >= $longitud || $linea[$temp_i] !== '`')) { $cierre_encontrado = true; // Verificar que no haya saltos de línea en el contenido if (strpos($contenido, "\n") === false) { // Almacenar el código en el array $almacen_codigos[] = $contenido; // Insertar marcador en lugar del código $resultado .= MARCADOR_CODIGO; $i = $temp_i; // Avanzar después del delimitador de cierre } else { // Tiene salto de línea, no es código válido $resultado .= substr($linea, $inicio, $temp_i - $inicio); $i = $temp_i; } break; } } // Acumular contenido $contenido .= $linea[$i]; $i++; } if (!$cierre_encontrado) { // No se encontró cierre $resultado .= substr($linea, $inicio, $i - $inicio); } // Retroceder uno porque el bucle for va a incrementar // y la $i se dejó justo en la posición correcta detrás del último ` $i--; } else { $resultado .= $linea[$i]; } } return $resultado; } function parsea_nueva_linea($linea) { $longitud = strlen($linea); // Si la cadena está vacía, retornar vacío if ($longitud === 0) { return $linea; } // Caso 1: Verificar si termina con barra invertida (\) y el anterior no es barra invertida if ($linea[$longitud - 1] === '\\') { // Si solo hay un caracter o el caracter anterior no es barra invertida if ($longitud === 1 || $linea[$longitud - 2] !== '\\') { // Reemplazar la última barra invertida por
    return substr($linea, 0, $longitud - 1) . '
    '; } } // Caso 2: Verificar si termina con al menos dos espacios if ($longitud >= 2 && $linea[$longitud - 1] === ' ') { $espacios = 0; // Contar espacios consecutivos desde el final for ($i = $longitud - 1; $i >= 0 && $linea[$i] === ' '; $i--) { $espacios++; } // Si hay al menos 2 espacios al final if ($espacios >= 2) { // Reemplazar los dos últimos espacios por
    return substr($linea, 0, $longitud - 2) . '
    '; } } // Si no se aplica ninguna regla, retornar la cadena original return $linea; } function escapa_caracteres($linea) { // Array de caracteres especiales de Markdown y sus entidades HTML $caracteres_especiales = [ '\\' => '\', // Barra invertida '`' => '`', // Backtick '*' => '*', // Asterisco '_' => '_', // Guión bajo '[' => '[', // Corchete izquierdo ']' => ']', // Corchete derecho '(' => '(', // Paréntesis izquierdo ')' => ')', // Paréntesis derecho '#' => '#', // Almohadilla '+' => '+', // Signo más '-' => '-', // Guión '.' => '.', // Punto '!' => '!', // Exclamación '|' => '|', // Barra vertical '~' => '~', // Tilde '^' => '^', // Acento circunflejo '{' => '{', // Llave izquierda '}' => '}', // Llave derecha '<' => '<', // Menor que (HTML) '>' => '>', // Mayor que (HTML) '&' => '&', // Ampersand (HTML) '"' => '"', // Comillas dobles (HTML) ]; $resultado = ''; $longitud = strlen($linea); $i = 0; while ($i < $longitud) { // Detectar si encontramos una etiqueta if (substr($linea, $i, 6) === '') { // Añadir la etiqueta de apertura $resultado .= ''; $i += 6; // Buscar la etiqueta de cierre correspondiente $pos_cierre = strpos($linea, '', $i); if ($pos_cierre === false) { // No hay cierre, copiar el resto y terminar $resultado .= substr($linea, $i); break; } // Copiar el contenido dentro de sin procesar $contenido_codigo = substr($linea, $i, $pos_cierre - $i); $resultado .= $contenido_codigo; // Añadir la etiqueta de cierre $resultado .= ''; $i = $pos_cierre + 7; continue; } // Para el texto fuera de , procesar secuencias de escape // Buscar \ seguido de un carácter especial if ($i < $longitud - 1 && $linea[$i] === '\\') { $caracter_siguiente = $linea[$i + 1]; // Verificar si el siguiente carácter está en nuestra lista de especiales if (isset($caracteres_especiales[$caracter_siguiente])) { // Reemplazar \caracter por la entidad HTML $resultado .= $caracteres_especiales[$caracter_siguiente]; $i += 2; // Saltar ambos caracteres continue; } } // Si no es una secuencia de escape, copiar el carácter tal cual $resultado .= $linea[$i]; $i++; } return $resultado; } function parsea_enlaces($linea, &$almacen_enlaces) { $resultado = ''; $longitud = strlen($linea); $i = 0; while ($i < $longitud) { // Buscamos un '[' que no esté escapado if ($linea[$i] === '[' && ($i === 0 || $linea[$i-1] !== '\\') && ($i === 0 || $linea[$i-1] !== '!')) { $inicio = $i; $i++; // Buscamos el cierre ']' que esté seguido inmediatamente por '(' $enlace_texto = ''; $encontrado_cierre = false; while ($i < $longitud && !$encontrado_cierre) { if ($linea[$i] === ']' && $i+1 < $longitud && $linea[$i+1] === '(') { $encontrado_cierre = true; $i += 2; // Pasamos el '](' break; } $enlace_texto .= $linea[$i]; $i++; } if ($encontrado_cierre) { // Buscamos el URL hasta el primer espacio o fin de línea $url = ''; $posicion_parentesis_inicio = $i - 1; // Posición del '(' while ($i < $longitud && $linea[$i] !== ' ' && $linea[$i] !== "\t" && $linea[$i] !== "\n") { $url .= $linea[$i]; $i++; } // Retrocedemos para encontrar el último ')' antes del espacio $j = $i - 1; $parentesis_cierre = -1; while ($j > $posicion_parentesis_inicio) { if ($linea[$j] === ')') { $parentesis_cierre = $j; break; } $j--; } if ($parentesis_cierre !== -1) { // Extraemos el URL correcto (sin el ')') $url = substr($linea, $posicion_parentesis_inicio + 1, $parentesis_cierre - $posicion_parentesis_inicio - 1); // Verificamos si hay atributos CSS opcionales $clases = []; $id = null; $posicion_actual = $parentesis_cierre + 1; // Comprobamos si hay '{' justo después del ')' if ($posicion_actual < $longitud && $linea[$posicion_actual] === '{') { $posicion_actual++; // Pasamos la '{' $contenido_llaves = ''; // Extraemos el contenido hasta la '}' while ($posicion_actual < $longitud && $linea[$posicion_actual] !== '}') { $contenido_llaves .= $linea[$posicion_actual]; $posicion_actual++; } if ($posicion_actual < $longitud && $linea[$posicion_actual] === '}') { $posicion_actual++; // Pasamos la '}' // Parseamos clases e ID $elementos = explode(' ', $contenido_llaves); foreach ($elementos as $elemento) { $elemento = trim($elemento); if ($elemento === '') continue; if ($elemento[0] === '.') { // Es una clase (quitamos el punto) $clases[] = substr($elemento, 1); } elseif ($elemento[0] === '#' && $id === null) { // Es un ID (quitamos el #, solo el primero cuenta) $id = substr($elemento, 1); } } } } // Construimos la etiqueta con el marcador en href $atributos = 'href="' . MARCADOR_URL_ENLACES . '"'; if (!empty($clases)) { $atributos .= ' class="' . htmlspecialchars(implode(' ', $clases), ENT_QUOTES | ENT_HTML5, 'UTF-8') . '"'; } if ($id !== null) { $atributos .= ' id="' . htmlspecialchars($id, ENT_QUOTES | ENT_HTML5, 'UTF-8') . '"'; } // Almacenamos la URL en el array para posterior restitución $almacen_enlaces[] = $url; // Añadimos la etiqueta completa al resultado (con el texto del enlace sin escapar) $resultado .= '' . $enlace_texto . ''; // Actualizamos la posición actual $i = $posicion_actual; } else { // No se encontró el cierre del URL, dejamos el texto original $resultado .= substr($linea, $inicio, $i - $inicio); } } else { // No se encontró el cierre '](', dejamos el texto original $resultado .= substr($linea, $inicio, $i - $inicio); } } else { // Carácter normal: lo añadimos tal cual y avanzamos uno $resultado .= $linea[$i]; $i++; } } return $resultado; } function parsea_imagenes($linea, &$almacen_imagenes) { $resultado = ''; $longitud = strlen($linea); $i = 0; while ($i < $longitud) { // Buscamos '![' que no esté escapado if ($linea[$i] === '!' && $i+1 < $longitud && $linea[$i+1] === '[' && ($i === 0 || $linea[$i-1] !== '\\')) { $inicio = $i; $i += 2; // Pasamos '![' // Buscamos el cierre ']' que esté seguido inmediatamente por '(' $alt_text = ''; $encontrado_cierre = false; while ($i < $longitud && !$encontrado_cierre) { if ($linea[$i] === ']' && $i+1 < $longitud && $linea[$i+1] === '(') { $encontrado_cierre = true; $i += 2; // Pasamos el '](' break; } $alt_text .= $linea[$i]; $i++; } if ($encontrado_cierre) { // Buscamos el URL hasta el primer espacio o fin de línea $url = ''; $posicion_parentesis_inicio = $i - 1; // Posición del '(' while ($i < $longitud && $linea[$i] !== ' ' && $linea[$i] !== "\t" && $linea[$i] !== "\n") { $url .= $linea[$i]; $i++; } // Retrocedemos para encontrar el último ')' antes del espacio $j = $i - 1; $parentesis_cierre = -1; while ($j > $posicion_parentesis_inicio) { if ($linea[$j] === ')') { $parentesis_cierre = $j; break; } $j--; } if ($parentesis_cierre !== -1) { // Extraemos el URL correcto (sin el ')') $url = substr($linea, $posicion_parentesis_inicio + 1, $parentesis_cierre - $posicion_parentesis_inicio - 1); // Verificamos si hay atributos CSS opcionales $clases = []; $id = null; $posicion_actual = $parentesis_cierre + 1; // Comprobamos si hay '{' justo después del ')' if ($posicion_actual < $longitud && $linea[$posicion_actual] === '{') { $posicion_actual++; // Pasamos la '{' $contenido_llaves = ''; // Extraemos el contenido hasta la '}' while ($posicion_actual < $longitud && $linea[$posicion_actual] !== '}') { $contenido_llaves .= $linea[$posicion_actual]; $posicion_actual++; } if ($posicion_actual < $longitud && $linea[$posicion_actual] === '}') { $posicion_actual++; // Pasamos la '}' // Parseamos clases e ID $elementos = explode(' ', $contenido_llaves); foreach ($elementos as $elemento) { $elemento = trim($elemento); if ($elemento === '') continue; if ($elemento[0] === '.') { // Es una clase (quitamos el punto) $clases[] = substr($elemento, 1); } elseif ($elemento[0] === '#' && $id === null) { // Es un ID (quitamos el #, solo el primero cuenta) $id = substr($elemento, 1); } } } } // Construimos la etiqueta $atributos = 'src="' . MARCADOR_IMAGENES_MD . '"'; $atributos .= ' alt="' . htmlspecialchars($alt_text, ENT_QUOTES | ENT_HTML5, 'UTF-8') . '"'; if (!empty($clases)) { $atributos .= ' class="' . htmlspecialchars(implode(' ', $clases), ENT_QUOTES | ENT_HTML5, 'UTF-8') . '"'; } if ($id !== null) { $atributos .= ' id="' . htmlspecialchars($id, ENT_QUOTES | ENT_HTML5, 'UTF-8') . '"'; } // Almacenamos la URL en el array para posterior restitución $almacen_imagenes[] = $url; // Añadimos la etiqueta completa al resultado $resultado .= ''; // Actualizamos la posición actual $i = $posicion_actual; } else { // No se encontró el cierre del URL, dejamos el texto original $resultado .= substr($linea, $inicio, $i - $inicio); } } else { // No se encontró el cierre '](', dejamos el texto original $resultado .= substr($linea, $inicio, $i - $inicio); } } else { // Carácter normal: lo añadimos tal cual y avanzamos uno $resultado .= $linea[$i]; $i++; } } return $resultado; } function parsea_negrita_cursiva($linea) { // Procesamos desde 3 asteriscos hasta 1 for ($num_asteriscos = 3; $num_asteriscos > 0; $num_asteriscos--) { $posiciones = []; $longitud = strlen($linea); $i = 0; // Primera pasada: detectar todas las secuencias exactas de $num_asteriscos asteriscos while ($i < $longitud) { // Si encontramos un asterisco, no está escapado y no es parte de una secuencia más larga if ($linea[$i] === '*' && ($i === 0 || $linea[$i-1] !== '\\') && ($i === 0 || $linea[$i-1] !== '*')) { $count = 0; $j = $i; // Contamos asteriscos consecutivos while ($j < $longitud && $linea[$j] === '*') { $count++; $j++; } // Si es exactamente el número que buscamos y no hay más asteriscos después if ($count == $num_asteriscos && ($j >= $longitud || $linea[$j] !== '*')) { $posiciones[] = $i; $i += $num_asteriscos; // Saltamos los asteriscos } else { $i++; } } else { $i++; } } // Asegurar que el número de posiciones sea par if (count($posiciones) % 2 != 0) { array_pop($posiciones); } // Si no hay posiciones, continuamos if (empty($posiciones)) { continue; } // Definir etiquetas según el número de asteriscos if ($num_asteriscos == 3) { $etiqueta_apertura = ''; $etiqueta_cierre = ''; } elseif ($num_asteriscos == 2) { $etiqueta_apertura = ''; $etiqueta_cierre = ''; } else { // 1 $etiqueta_apertura = ''; $etiqueta_cierre = ''; } // Segunda pasada: construir la nueva cadena $resultado = ''; $cursor = 0; $etiqueta_abierta = false; foreach ($posiciones as $posicion) { // Copiar desde el cursor hasta la posición actual $resultado .= substr($linea, $cursor, $posicion - $cursor); // Insertar etiqueta correspondiente if (!$etiqueta_abierta) { $resultado .= $etiqueta_apertura; } else { $resultado .= $etiqueta_cierre; } // Alternar estado $etiqueta_abierta = !$etiqueta_abierta; // Mover el cursor después de los asteriscos $cursor = $posicion + $num_asteriscos; } // Copiar el resto de la cadena if ($cursor < $longitud) { $resultado .= substr($linea, $cursor); } // Actualizar la línea para el siguiente nivel $linea = $resultado; } return $linea; } function parsea_seccion($linea) { $patron = '/^S(\d+)\.\s/'; $reemplazo = '$1.- '; return preg_replace($patron, $reemplazo, $linea, 1); } function recoloca_elementos($linea, &$almacen_codigos, &$almacen_enlaces, &$almacen_imagenes) { // Recoloca los códigos en línea: $num_marcadores = substr_count($linea, MARCADOR_CODIGO); if ($num_marcadores > 0 && count($almacen_codigos) >= $num_marcadores) { for ($i = 0; $i < $num_marcadores; $i++) { $codigo = array_shift($almacen_codigos); $codigo_procesado = '' . htmlspecialchars($codigo, ENT_QUOTES | ENT_HTML5, 'UTF-8') . ''; $posicion = strpos($linea, MARCADOR_CODIGO); if ($posicion !== false) { $linea = substr_replace($linea, $codigo_procesado, $posicion, strlen(MARCADOR_CODIGO)); } } } // Recoloca los enlaces: $num_marcadores_enlaces = substr_count($linea, MARCADOR_URL_ENLACES); if ($num_marcadores_enlaces > 0 && count($almacen_enlaces) >= $num_marcadores_enlaces) { for ($i = 0; $i < $num_marcadores_enlaces; $i++) { $url = array_shift($almacen_enlaces); $posicion = strpos($linea, MARCADOR_URL_ENLACES); if ($posicion !== false) { $linea = substr_replace($linea, $url, $posicion, strlen(MARCADOR_URL_ENLACES)); } } } // Recoloca las imágenes: $num_marcadores_imagenes = substr_count($linea, MARCADOR_IMAGENES_MD); if ($num_marcadores_imagenes > 0 && count($almacen_imagenes) >= $num_marcadores_imagenes) { for ($i = 0; $i < $num_marcadores_imagenes; $i++) { $url = array_shift($almacen_imagenes); $posicion = strpos($linea, MARCADOR_IMAGENES_MD); if ($posicion !== false) { $linea = substr_replace($linea, $url, $posicion, strlen(MARCADOR_IMAGENES_MD)); } } } return $linea; } ?>