[FIX] широкие таблицы переносятся по словам и не вылезают за поле
This commit is contained in:
@@ -80,18 +80,36 @@ def _collect_rows(
|
|||||||
return pos, rows, aligns
|
return pos, rows, aligns
|
||||||
|
|
||||||
|
|
||||||
|
_STRETCH = ">{\\raggedright\\arraybackslash}X"
|
||||||
|
|
||||||
|
|
||||||
def _tabular(rows: list[list[str]], aligns: list[str]) -> str:
|
def _tabular(rows: list[list[str]], aligns: list[str]) -> str:
|
||||||
|
"""Табличное окружение для строк с заданным выравниванием колонок.
|
||||||
|
|
||||||
|
Колонка, выровненная по левому краю, считается текстовой: её содержимое
|
||||||
|
должно переноситься по словам, иначе широкая таблица уезжает за правое
|
||||||
|
поле страницы. Такие колонки верстаются растягиваемым типом ``X``, а вся
|
||||||
|
таблица — окружением ``tabularx`` шириной в текстовое поле. Колонки по
|
||||||
|
центру и по правому краю (обычно числовые) остаются узкими.
|
||||||
|
|
||||||
|
Если текстовых колонок нет, растягивать нечего и используется обычный
|
||||||
|
``tabular``: ``tabularx`` без единой ``X``-колонки не собирается.
|
||||||
|
"""
|
||||||
cols = max(len(row) for row in rows)
|
cols = max(len(row) for row in rows)
|
||||||
for row in rows:
|
for row in rows:
|
||||||
row.extend([""] * (cols - len(row)))
|
row.extend([""] * (cols - len(row)))
|
||||||
|
|
||||||
spec = list(aligns[:cols]) + ["l"] * max(0, cols - len(aligns))
|
spec = list(aligns[:cols]) + ["l"] * max(0, cols - len(aligns))
|
||||||
col_spec = "|".join(spec)
|
stretchable = "l" in spec
|
||||||
lines = [f"\\begin{{tabular}}{{|{col_spec}|}}", "\\hline"]
|
col_spec = "|".join(_STRETCH if col == "l" else col for col in spec)
|
||||||
|
|
||||||
|
env = "tabularx" if stretchable else "tabular"
|
||||||
|
width = "{\\textwidth}" if stretchable else ""
|
||||||
|
lines = [f"\\begin{{{env}}}{width}{{|{col_spec}|}}", "\\hline"]
|
||||||
for row in rows:
|
for row in rows:
|
||||||
lines.append(" & ".join(row) + " \\\\")
|
lines.append(" & ".join(row) + " \\\\")
|
||||||
lines.append("\\hline")
|
lines.append("\\hline")
|
||||||
lines.append("\\end{tabular}")
|
lines.append(f"\\end{{{env}}}")
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+42
-2
@@ -1,5 +1,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from md2gost.handlers.table import _STRETCH
|
||||||
from md2gost.latex.titlepages import TITLEPAGES
|
from md2gost.latex.titlepages import TITLEPAGES
|
||||||
from md2gost.parser import Parser
|
from md2gost.parser import Parser
|
||||||
from md2gost.render import Renderer
|
from md2gost.render import Renderer
|
||||||
@@ -12,6 +13,11 @@ def render(md: str) -> str:
|
|||||||
return Renderer(parser.tokens, parser.front_matter).render()
|
return Renderer(parser.tokens, parser.front_matter).render()
|
||||||
|
|
||||||
|
|
||||||
|
def body(tex: str) -> str:
|
||||||
|
"""Тело документа без преамбулы: в преамбуле свои tabularx-макросы."""
|
||||||
|
return tex.split("\\setcounter{page}{2}", 1)[1]
|
||||||
|
|
||||||
|
|
||||||
def render_fm(extra: str, body: str = "Текст.\n") -> str:
|
def render_fm(extra: str, body: str = "Текст.\n") -> str:
|
||||||
head = f"---\nназвание: Тестовая работа\nтип: Отчёт\n{extra}---\n\n"
|
head = f"---\nназвание: Тестовая работа\nтип: Отчёт\n{extra}---\n\n"
|
||||||
parser = Parser(head + body)
|
parser = Parser(head + body)
|
||||||
@@ -123,7 +129,7 @@ def test_table_with_caption() -> None:
|
|||||||
tex = render(TABLE_MD)
|
tex = render(TABLE_MD)
|
||||||
assert "\\begin{table}[H]" in tex
|
assert "\\begin{table}[H]" in tex
|
||||||
assert "\\caption{Сравнение скорости}" in tex
|
assert "\\caption{Сравнение скорости}" in tex
|
||||||
assert "\\begin{tabular}{|l|c|r|}" in tex
|
assert "\\begin{tabularx}{\\textwidth}" in tex
|
||||||
assert "\\textbf{1} & 0.027852 & 0.000285 \\\\" in tex
|
assert "\\textbf{1} & 0.027852 & 0.000285 \\\\" in tex
|
||||||
|
|
||||||
|
|
||||||
@@ -136,7 +142,7 @@ def test_table_caption_number_is_discarded() -> None:
|
|||||||
def test_table_without_caption_stays_bare() -> None:
|
def test_table_without_caption_stays_bare() -> None:
|
||||||
tex = render("| a | b |\n| --- | --- |\n| 1 | 2 |\n")
|
tex = render("| a | b |\n| --- | --- |\n| 1 | 2 |\n")
|
||||||
assert "\\begin{table}" not in tex
|
assert "\\begin{table}" not in tex
|
||||||
assert "\\begin{tabular}{|l|l|}" in tex
|
assert "\\begin{tabularx}{\\textwidth}" in tex
|
||||||
|
|
||||||
|
|
||||||
def test_table_caption_paragraph_is_not_duplicated() -> None:
|
def test_table_caption_paragraph_is_not_duplicated() -> None:
|
||||||
@@ -365,3 +371,37 @@ def test_listing_unsupported_language_falls_back_to_plain() -> None:
|
|||||||
tex = render("```yaml\nkey: value\n```\n")
|
tex = render("```yaml\nkey: value\n```\n")
|
||||||
assert "language=" not in tex
|
assert "language=" not in tex
|
||||||
assert "key: value" in tex
|
assert "key: value" in tex
|
||||||
|
|
||||||
|
|
||||||
|
WIDE_TABLE_MD = (
|
||||||
|
"Таблица 1 —— Сценарии интеграции\n\n"
|
||||||
|
"| Сценарий | Событие | Что делает |\n"
|
||||||
|
"| :--- | :--- | :--- |\n"
|
||||||
|
"| build.yml | отправка изменений | Собирает образ Docker целиком |\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_wide_table_uses_tabularx_so_it_fits_the_page() -> None:
|
||||||
|
"""Колонки с текстом должны переноситься, а не вылезать за поле."""
|
||||||
|
tex = render(WIDE_TABLE_MD)
|
||||||
|
assert "\\begin{tabularx}{\\textwidth}" in tex
|
||||||
|
assert "\\begin{tabular}{" not in tex
|
||||||
|
|
||||||
|
|
||||||
|
def test_table_left_columns_become_stretchable() -> None:
|
||||||
|
tex = render(WIDE_TABLE_MD)
|
||||||
|
assert tex.count(">{\\raggedright\\arraybackslash}X") == 3
|
||||||
|
|
||||||
|
|
||||||
|
def test_table_keeps_center_columns_narrow() -> None:
|
||||||
|
"""Числовые колонки не должны растягиваться — растягивается только текст."""
|
||||||
|
tex = render(TABLE_MD)
|
||||||
|
assert "\\begin{tabularx}{\\textwidth}" in tex
|
||||||
|
assert tex.count(">{\\raggedright\\arraybackslash}X") == 1
|
||||||
|
assert f"{{|{_STRETCH}|c|r|}}" in tex
|
||||||
|
|
||||||
|
|
||||||
|
def test_table_without_left_columns_stays_plain_tabular() -> None:
|
||||||
|
tex = body(render("| a | b |\n| :---: | :---: |\n| 1 | 2 |\n"))
|
||||||
|
assert "\\begin{tabular}{|c|c|}" in tex
|
||||||
|
assert "\\begin{tabularx}" not in tex
|
||||||
|
|||||||
Reference in New Issue
Block a user