Dónde puede cortarse una línea: el algoritmo Unicode detrás de cada párrafo ajustado
Publicado el 1/7/2025 · 13 min de lectura · Herramientas de texto e idioma
Daniel Okonkwo — Desarrollador front-end y redactor de Tecnología en OneKitly
Rendimiento web · Formatos de archivo
Verificado con 5 fuentes
Cortar texto partiendo por los espacios es incorrecto en la mayoría de los sistemas de escritura, y Unicode especifica el procedimiento correcto: UAX #14, el algoritmo de corte de línea. Asigna a cada punto de código una clase de corte y luego aplica reglas numeradas a cada par de clases. Un espacio duro U+00A0 y un espacio fino duro U+202F son de clase GL, pegamento, y el corte después de ellos está prohibido: una implementación conforme no encontró ninguna oportunidad de corte dentro de cost·100·USD escrito con espacios duros, donde la versión ordinaria tiene dos. Un guion blando U+00AD es de clase BA, así que el corte ahí está permitido y el guion solo se hace visible si se toma. Un espacio de ancho cero U+200B es de clase ZW: permite el corte y no muestra nada, que es como se ajusta un texto sin espacio alguno. Un guion duro U+2011 es pegamento, mientras que el guion-menos ordinario U+002D es de clase HY y sí permite cortar tras él. El japonés no necesita espacios: cada ideograma es de clase ID y el corte se permite entre casi cualquier par. El tailandés es de clase SA y no tiene ninguna sin análisis por diccionario.
«Cortar en los espacios» falla en la mayoría de los sistemas de escritura. UAX #14 da a cada carácter una clase de corte de línea; buscamos las nuestras en Unicode 17.0.0 y ejecutamos una implementación conforme sobre espacios duros, guiones blandos, espacios de ancho cero, URL, japonés y tailandés.
«Cortar en los espacios» describe una sola familia de escrituras
Corta por el espacio y una frase japonesa vuelve como un solo token: 日本語のテキストは、空白で区切らない no tiene ningún espacio, ni tampoco su equivalente tailandés. Ambas se ajustan perfectamente en pantalla, porque la regla no habla de espacios. Pasa una implementación conforme sobre esa frase japonesa: informa de dieciséis oportunidades de corte en dieciocho caracteres — una entre casi cada par — negándose a cortar antes de la coma ideográfica o del punto. La misma implementación sobre la frase tailandesa informa de cero, por una razón a la que volvemos más abajo.
El caso de la escritura latina tampoco es seguro. Escrito con espacios ordinarios, cost 100 USD ofrece dos oportunidades de corte; escrito con espacios duros U+00A0 no ofrece ninguna, y un ajustador que corta por espacios lo devuelve como un único token largo que no puede partir. Los caracteres se ven idénticos. La diferencia vive en los puntos de código, y cualquier ajustador que lea solo las formas producirá una línea demasiado larga o un corte tipográficamente incorrecto.
UAX #14: una clase por carácter y luego reglas sobre pares
La base de datos de caracteres Unicode incluye un fichero LineBreak.txt que asigna una clase de dos letras a cada punto de código. Analizamos la versión actual — LineBreak-17.0.0.txt, de 29 de julio de 2025 — y define 49 clases distintas en 3 654 rangos. La mayor con diferencia es ID, ideográfica, con 172 561 puntos de código; AL, alfabética ordinaria, cubre 26 954; las interesantes son diminutas, GL con 41 puntos de código, QU con 39, WJ con 2 y ZW con exactamente 1. El algoritmo recorre luego la cadena y consulta una regla para cada par de clases adyacentes.
Las reglas están numeradas y divididas en dos grupos. Unas pocas no son modificables: una implementación no puede cambiarlas y seguir reclamando conformidad. Las demás, desde LB12a en adelante, son un valor por defecto razonable que una implementación puede mejorar. LB12 prohíbe cortar tras el pegamento. LB13 lo prohíbe antes del corchete de cierre, el signo de exclamación, la barra y la clase de puntuación de cierre, incluso tras espacios. LB18 permite cortar tras un espacio. LB21 lo prohíbe antes de un guion o de un carácter de corte posterior. Cuando los pares chocan, gana la regla de número más bajo, y por eso la especificación es una lista y no una tabla.
Cinco caracteres invisibles que deciden dónde caen las líneas
El espacio duro y el espacio fino duro son ambos de clase GL. Ocupan ancho y prohíben el corte, que es lo que mantiene un número con su unidad y un tratamiento con su apellido. El unificador de palabras U+2060 es de clase WJ y va más lejos: ni corte antes ni después, y ningún ancho. El espacio de ancho cero U+200B es lo contrario: clase ZW, sin ancho, corte permitido. Y el guion blando U+00AD es de clase BA, un carácter de corte posterior que no representa nada hasta que se usa el corte, momento en que el motor de renderizado aporta un guion.
La prueba del guion blando hace concreto el comportamiento. Tomamos un compuesto alemán de 63 caracteres e insertamos cinco guiones blandos: longitud almacenada 68, longitud visible 63. Con ancho 99 se muestra como una sola palabra sin cortar de 63 caracteres. Con ancho 30 cabe en tres líneas, con ancho 20 en cuatro, y el guion aparece solo al final de cada línea cortada — nunca en medio. Quita los guiones blandos y la misma palabra con ancho 20 vuelve como una sola línea de 63 caracteres que desborda, porque no hay ningún sitio donde el algoritmo tenga permitido cortar.
El mismo truco rescata escrituras que el algoritmo no sabe segmentar por sí solo. El tailandés es de clase SA, dependiente de contexto complejo, y UAX #14 dice claramente que las series de estos caracteres exigen análisis morfológico, que sin él no se hallará ninguna oportunidad de corte y que una implementación que carezca de él debería tratarlos como letras ordinarias. Nuestra implementación informó de cero oportunidades en una frase tailandesa de 23 caracteres. Insertar tres espacios de ancho cero en las fronteras de palabra produjo exactamente tres — invisibles en el texto representado, decisivas para el ajuste.
Las URL se cortan en sitios que no elegiste
Dale una URL al algoritmo y la respuesta es más interesante que «en ningún sitio» o «en cualquiera». Nuestra dirección de prueba produjo siete oportunidades de corte: tras la doble barra, tras cada barra simple, tras cada guion del camino y tras el signo de interrogación. Nunca antes de una barra — LB13 lo prohíbe de plano, incluso tras un espacio — y nunca en los puntos del nombre de host, porque el punto es de clase IS y LB15d rechaza cortar antes de él. El resultado es que una URL ajustada conserva sus separadores al final de la línea, donde el lector ve que la línea continúa.
El peligro es lo que hagas con ese corte. Un ajuste blando es una decisión de visualización: la cadena en memoria no cambia, y recomponer el párrafo a otro ancho solo mueve el corte. Un ajuste duro inserta un carácter de salto de línea real en los datos. Cortamos duramente la URL de prueba a 24 columnas; volviendo a unir las líneas sin nada, la URL vuelve byte a byte, pero uniéndolas con un espacio — que es lo que hará un cliente de correo, una aplicación de chat o un copiado descuidado — se obtienen cuatro fragmentos separados por espacios, y el enlace está muerto. Nunca cortes duramente algo que deba seguir siendo un solo token.
El ajuste duro destruye la recomposición — medido
Ajustamos un párrafo a 32 columnas, guardamos el resultado con saltos de línea reales y luego lo reajustamos a 20 sin deshacerlo antes. La salida fueron siete líneas, varias mucho más cortas de 20 caracteres, porque cada línea dura se reajustó aislada y los finales irregulares no pudieron rellenarse. Deshaciéndolo primero — uniendo las líneas con un espacio — y ajustando después a 20 salieron cinco líneas llenas. Esa diferencia es el coste del ajuste duro: el texto ya no sabe qué finales de línea eran del autor y cuáles del renderizador anterior.
De ahí viene también el famoso ancho fijo del correo electrónico, y el número que se cita no es el de la especificación. La RFC 5322 dice que cada línea NO DEBE superar los 998 caracteres y NO DEBERÍA superar los 78, sin contar el CRLF; los 998 existen porque implementaciones de transporte rechazan más de 1 000 caracteres por línea, y los 78 porque el software de visualización destroza cualquier cosa más larga. La RFC 3676, que define format=flowed, repite los 78 y explica que no son 79 ni 80 porque la última columna suele reservarse a un indicador de corte. Los 72 que usaban de verdad los clientes de correo son una convención encima, que deja sitio para varios niveles de marcas de cita en las respuestas.
Format=flowed es el compromiso que hizo soportable el correo de ancho fijo: una línea terminada en un espacio antes de su CRLF es un corte blando que el lector puede recomponer, mientras que una línea sin ese espacio es la del autor. Es texto ordinario para un cliente que jamás ha oído hablar de ello y texto recomponible para uno que sí. La lección general va más allá del correo: guarda el párrafo, no las líneas, y deja que el último renderizador decida dónde caen los cortes.
Lo que CSS te deja cambiar y lo que no
CSS Text Level 3 expone el algoritmo mediante cinco propiedades, y conviene conocer sus conjuntos de valores exactos. white-space admite normal, pre, nowrap, pre-wrap, break-spaces o pre-line, y por defecto vale normal. word-break admite normal, keep-all, break-all o break-word. line-break admite auto, loose, normal, strict o anywhere, y controla con cuánta severidad se aplica el algoritmo a la puntuación de Asia Oriental. hyphens admite none, manual o auto, con manual por defecto. overflow-wrap admite normal, break-word o anywhere. La especificación cita UAX #14 por su nombre para las clases subyacentes.
Dos detalles de esa lista son fáciles de equivocar. Como hyphens vale manual por defecto, los guiones blandos ya presentes en tu texto se respetan sin ningún CSS — y hyphens: none los suprime, pero la especificación dice explícitamente que eso no suprime las oportunidades de corte que ofrecen caracteres visibles como U+002D y U+2010. Y overflow-wrap: anywhere es un último recurso, no un arreglo: permite cortar en cualquier carácter cuando una palabra desbordaría, lo que para el compuesto de 63 caracteres de arriba significa un corte en medio de un morfema donde un guion blando habría dado uno correcto.
Los espacios tipográficos no son decoración
La puntuación francesa es la demostración más clara de que estos puntos de código hacen trabajo real. Ajustamos dos veces la misma frase francesa: una con espacios ordinarios en todas partes, otra con los espacios finos duros correctos ante la puntuación alta y un espacio duro ante la unidad. La primera versión ofrecía catorce oportunidades de corte, incluida una entre un número y su grupo de millares y otra antes de una comilla angular de cierre. La segunda ofrecía nueve, y ninguna separaba un signo de la palabra a la que pertenece. Mismo texto visible, conjunto distinto de cortes legales.
El mismo razonamiento va mucho más allá del francés. Un número y su unidad, una abreviatura y el nombre que la sigue, una cifra y un signo de porcentaje, un ordinal y su sustantivo: todos se leen mal si un corte cae entre ellos, y todos se arreglan con un carácter invisible en vez de con una regla en el ajustador. Nuestra herramienta de ajuste deja intactos esos caracteres, que es el único comportamiento correcto: limpiarlos, como hace un normalizador de espacios ingenuo, da en silencio al renderizador permiso para cortar donde el autor lo prohibía.
| Carácter | Punto de código | Clase | ¿Corte después? | ¿Visible? |
|---|---|---|---|---|
| Espacio | U+0020 | SP | Sí | Sí, como ancho en blanco |
| Espacio duro | U+00A0 | GL | No | Sí, como ancho en blanco |
| Espacio fino duro | U+202F | GL | No | Sí, más estrecho |
| Guion-menos | U+002D | HY | Sí | Siempre |
| Guion duro | U+2011 | GL | No | Siempre |
| Guion blando | U+00AD | BA | Sí | Solo si se toma el corte |
| Espacio de ancho cero | U+200B | ZW | Sí | Nunca |
| Unificador de palabras | U+2060 | WJ | No, ni antes de él | Nunca |
| Barra en una URL | U+002F | SY | Sí después, nunca antes (LB13) | Siempre |
| Ideograma CJK | U+4E00 y 172 560 más | ID | Sí, entre casi cualquier par | Siempre |
| Letra tailandesa | U+0E01 | SA | Solo con análisis por diccionario | Siempre |
Preguntas frecuentes
- ¿Por qué mi texto se corta en medio de un número?
- Porque el separador de grupos es un espacio ordinario, clase SP, y la regla LB18 permite cortar tras cualquier espacio. Sustitúyelo por un espacio duro U+00A0 o un espacio fino duro U+202F, ambos de clase GL, y la oportunidad desaparece: nuestra frase francesa pasó de catorce oportunidades de corte a nueve con los espacios tipográficos, y ninguna de las nueve restantes caía dentro de un número.
- ¿Qué diferencia hay entre un guion blando y un espacio de ancho cero?
- Ambos permiten cortar y ninguno es visible hasta que se usa, pero el guion blando U+00AD deja un guion cuando el corte se toma y el espacio de ancho cero U+200B no deja nada. Usa el guion blando dentro de una palabra, donde el guion es la tipografía correcta; usa el espacio de ancho cero entre unidades que no deben ganar guion, como partes de un identificador largo o de una URL, y en escrituras que nunca usan guion.
- ¿Debo cortar duramente el texto a 72 u 80 caracteres?
- Solo cuando lo exija un protocolo. La RFC 5322 fija un límite duro de 998 caracteres por línea y una recomendación de 78; los 72 que usaban los clientes de correo dejan sitio para las marcas de cita en las respuestas y son una convención, no una especificación. El ajuste duro cuesta la recomposición: reajustar a 20 un párrafo ya cortado duramente a 32 columnas dio siete líneas irregulares, frente a cinco líneas llenas si lo deshacíamos primero.
- ¿Cómo evito que una URL larga desborde su contenedor?
- No insertando saltos de línea. El algoritmo ya ofrece cortes tras la doble barra, tras cada barra y tras cada guion — siete oportunidades en nuestra dirección de prueba — así que un ajuste blando suele bastar. Si el contenedor es más estrecho que la serie irrompible más larga, añade overflow-wrap: anywhere para ese elemento o inserta espacios de ancho cero, que no cuestan nada al copiar. Una URL cortada duramente y reunida con un espacio ya no es una URL.
- ¿El algoritmo sabe dónde acaban las palabras tailandesas?
- No, y UAX #14 lo dice. Los caracteres tailandeses, lao y jemer son de clase SA, dependiente de contexto complejo, y el anexo indica que sus series exigen análisis morfológico, que sin él no se hallará ninguna oportunidad de corte y que una implementación que carezca de él debería tratarlos como letras ordinarias. Nuestra implementación conforme halló cero oportunidades en una frase tailandesa de 23 caracteres, y exactamente tres tras insertar tres espacios de ancho cero.
Artículos que podrían interesarte
Todas las guías →Herramientas relacionadas
Fuentes
- Unicode Consortium — Unicode Standard Annex #14: Unicode Line Breaking Algorithm — line break classes and rules LB1–LB31
- Unicode Consortium — LineBreak.txt in the Unicode Character Database — the authoritative class for every code point (17.0.0, 2025-07-29)
- W3C — CSS Text Module Level 3 — white-space, word-break, line-break, hyphens and overflow-wrap
- IETF (RFC Editor) — RFC 5322 — Internet Message Format, section 2.1.1 Line Length Limits (998 characters MUST, 78 SHOULD)
- IETF (RFC Editor) — RFC 3676 — The Text/Plain Format and DelSp Parameters (format=flowed)
¿Has detectado un error en este artículo?