markdown-gost 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. markdown_gost/__init__.py +5 -0
  2. markdown_gost/cli/__init__.py +0 -0
  3. markdown_gost/cli/__main__.py +191 -0
  4. markdown_gost/cli/commands/__init__.py +0 -0
  5. markdown_gost/cli/commands/import_cmd.py +134 -0
  6. markdown_gost/config/__init__.py +0 -0
  7. markdown_gost/config/errors.py +8 -0
  8. markdown_gost/config/loader.py +59 -0
  9. markdown_gost/config/presets/__init__.py +0 -0
  10. markdown_gost/config/presets/default.yaml +94 -0
  11. markdown_gost/config/presets/gost-7-32-2017.yaml +124 -0
  12. markdown_gost/config/presets/mirea-practice.yaml +124 -0
  13. markdown_gost/config/schema.py +688 -0
  14. markdown_gost/config/units.py +61 -0
  15. markdown_gost/convert.py +84 -0
  16. markdown_gost/core/__init__.py +0 -0
  17. markdown_gost/core/ast/__init__.py +77 -0
  18. markdown_gost/core/ast/nodes.py +253 -0
  19. markdown_gost/core/parser/__init__.py +358 -0
  20. markdown_gost/core/parser/_marko_ext.py +155 -0
  21. markdown_gost/core/parser/attrs.py +191 -0
  22. markdown_gost/image_placeholder.py +78 -0
  23. markdown_gost/import_/__init__.py +21 -0
  24. markdown_gost/import_/image_handler.py +82 -0
  25. markdown_gost/import_/listing_detector.py +92 -0
  26. markdown_gost/import_/metrics.py +36 -0
  27. markdown_gost/import_/pandoc_runner.py +97 -0
  28. markdown_gost/import_/pdf_prepass.py +164 -0
  29. markdown_gost/import_/pipeline.py +345 -0
  30. markdown_gost/import_/postprocessor/README.md +56 -0
  31. markdown_gost/import_/postprocessor/__init__.py +55 -0
  32. markdown_gost/import_/postprocessor/_common.py +18 -0
  33. markdown_gost/import_/postprocessor/caption_folder.py +212 -0
  34. markdown_gost/import_/postprocessor/heading_normalizer.py +39 -0
  35. markdown_gost/import_/postprocessor/html_table_converter.py +348 -0
  36. markdown_gost/import_/postprocessor/inline_normalizer.py +50 -0
  37. markdown_gost/import_/postprocessor/listing_wrapper.py +130 -0
  38. markdown_gost/import_/postprocessor/unnumbered_heading_detector.py +57 -0
  39. markdown_gost/import_/result.py +61 -0
  40. markdown_gost/import_/semantic_docx.py +72 -0
  41. markdown_gost/metrics_compat.py +38 -0
  42. markdown_gost/output/__init__.py +0 -0
  43. markdown_gost/output/pdf_writer.py +183 -0
  44. markdown_gost/preview/__init__.py +34 -0
  45. markdown_gost/preview/builder.py +2316 -0
  46. markdown_gost/preview/html.py +1350 -0
  47. markdown_gost/preview/model.py +298 -0
  48. markdown_gost/preview_json.py +59 -0
  49. markdown_gost/render/__init__.py +14 -0
  50. markdown_gost/render/_assets/__init__.py +0 -0
  51. markdown_gost/render/_assets/mml2omml.xsl +3822 -0
  52. markdown_gost/render/bibliography.py +202 -0
  53. markdown_gost/render/document_factory.py +140 -0
  54. markdown_gost/render/latex_math.py +235 -0
  55. markdown_gost/render/layout_tracker.py +65 -0
  56. markdown_gost/render/numberer.py +102 -0
  57. markdown_gost/render/paragraph_sizer.py +395 -0
  58. markdown_gost/render/references.py +91 -0
  59. markdown_gost/render/render_index.py +126 -0
  60. markdown_gost/render/renderer.py +358 -0
  61. markdown_gost/renderable/__init__.py +25 -0
  62. markdown_gost/renderable/_oxml.py +40 -0
  63. markdown_gost/renderable/appendix.py +109 -0
  64. markdown_gost/renderable/base.py +84 -0
  65. markdown_gost/renderable/bibliography.py +58 -0
  66. markdown_gost/renderable/caption.py +150 -0
  67. markdown_gost/renderable/equation.py +358 -0
  68. markdown_gost/renderable/factory.py +238 -0
  69. markdown_gost/renderable/heading.py +247 -0
  70. markdown_gost/renderable/image.py +241 -0
  71. markdown_gost/renderable/list.py +306 -0
  72. markdown_gost/renderable/listing.py +514 -0
  73. markdown_gost/renderable/page_break.py +47 -0
  74. markdown_gost/renderable/paragraph.py +543 -0
  75. markdown_gost/renderable/table.py +797 -0
  76. markdown_gost/renderable/thematic_break.py +18 -0
  77. markdown_gost/storage/__init__.py +46 -0
  78. markdown_gost/storage/base.py +68 -0
  79. markdown_gost/storage/fs.py +96 -0
  80. markdown_gost/templates/__init__.py +202 -0
  81. markdown_gost/templates/_placeholder.py +41 -0
  82. markdown_gost/templates/_preview.py +93 -0
  83. markdown_gost/templates/_registry.py +128 -0
  84. markdown_gost/templates/_schema.py +184 -0
  85. markdown_gost/templates/base.docx +0 -0
  86. markdown_gost/templates/content/__init__.py +19 -0
  87. markdown_gost/templates/content/preview.py +77 -0
  88. markdown_gost/templates/content/render.py +241 -0
  89. markdown_gost/templates/content/schema.yaml +30 -0
  90. markdown_gost/templates/titlepage_university/__init__.py +28 -0
  91. markdown_gost/templates/titlepage_university/preview.py +108 -0
  92. markdown_gost/templates/titlepage_university/render.py +407 -0
  93. markdown_gost/templates/titlepage_university/schema.yaml +131 -0
  94. markdown_gost-0.2.0.dist-info/METADATA +104 -0
  95. markdown_gost-0.2.0.dist-info/RECORD +98 -0
  96. markdown_gost-0.2.0.dist-info/WHEEL +4 -0
  97. markdown_gost-0.2.0.dist-info/entry_points.txt +3 -0
  98. markdown_gost-0.2.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,5 @@
1
+ from markdown_gost.convert import convert
2
+
3
+ __version__ = "0.2.0"
4
+
5
+ __all__ = ["__version__", "convert"]
File without changes
@@ -0,0 +1,191 @@
1
+ """markdown-gost CLI (T020).
2
+
3
+ Commands: ``convert``, ``validate``. Exit codes: ``0`` ok, ``1`` user error
4
+ (config/parse), ``2`` system error / usage error.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ import sys
11
+ from collections.abc import Callable
12
+ from pathlib import Path
13
+ from typing import Any, NoReturn, cast
14
+
15
+ import click
16
+ import yaml
17
+
18
+ from markdown_gost import __version__
19
+ from markdown_gost.cli.commands.import_cmd import import_command
20
+ from markdown_gost.config.errors import ConfigError
21
+ from markdown_gost.config.loader import load_config_from_path, load_config_from_string
22
+ from markdown_gost.config.schema import Config
23
+ from markdown_gost.convert import Format
24
+ from markdown_gost.convert import convert as convert_pipeline
25
+ from markdown_gost.core.parser import parse as parse_markdown
26
+ from markdown_gost.storage import get_storage
27
+
28
+ _DEFAULT_CONFIG = "preset: default\n"
29
+ _VALID_FORMATS: tuple[str, ...] = ("docx", "pdf")
30
+ _LOGGER = logging.getLogger("markdown_gost.cli")
31
+
32
+
33
+ def _resolve_format(explicit: str | None, output: Path | None) -> Format:
34
+ if explicit is not None:
35
+ return cast(Format, explicit.lower())
36
+ if output is not None:
37
+ suffix = output.suffix.lstrip(".").lower()
38
+ if suffix in _VALID_FORMATS:
39
+ return cast(Format, suffix)
40
+ return "docx"
41
+
42
+
43
+ def _load_config(config_path: Path | None) -> Config:
44
+ if config_path is None:
45
+ return load_config_from_string(_DEFAULT_CONFIG)
46
+ return load_config_from_path(config_path)
47
+
48
+
49
+ def _user_error(message: str) -> NoReturn:
50
+ raise click.ClickException(message)
51
+
52
+
53
+ def _system_error(message: str) -> NoReturn:
54
+ click.echo(f"Error: {message}", err=True)
55
+ sys.exit(2)
56
+
57
+
58
+ def _execute(action: Callable[[], Any]) -> Any:
59
+ """Run *action* and translate exceptions to documented exit codes.
60
+
61
+ ``ConfigError`` / ``ValueError`` (incl. pydantic ``ValidationError``) /
62
+ ``yaml.YAMLError`` → exit 1. Anything else → exit 2.
63
+ """
64
+
65
+ try:
66
+ return action()
67
+ except click.ClickException:
68
+ raise
69
+ except (ConfigError, ValueError, yaml.YAMLError) as exc:
70
+ _user_error(str(exc))
71
+ except Exception as exc:
72
+ _LOGGER.debug("system error", exc_info=True)
73
+ _system_error(str(exc))
74
+
75
+
76
+ @click.group(context_settings={"help_option_names": ["-h", "--help"]})
77
+ @click.version_option(__version__, prog_name="markdown-gost")
78
+ @click.option(
79
+ "--verbose",
80
+ "-v",
81
+ is_flag=True,
82
+ help="Enable DEBUG-level logging.",
83
+ )
84
+ def cli(verbose: bool) -> None:
85
+ """markdown-gost — Markdown → DOCX/PDF по ГОСТ.
86
+
87
+ Пресет ГОСТа задаётся в YAML-конфиге, не флагом CLI (см. ADR-0004).
88
+ """
89
+
90
+ level = logging.DEBUG if verbose else logging.INFO
91
+ logging.basicConfig(
92
+ level=level,
93
+ format="%(levelname)s %(name)s: %(message)s",
94
+ force=True,
95
+ )
96
+
97
+
98
+ @cli.command("convert")
99
+ @click.argument(
100
+ "input_path",
101
+ metavar="INPUT",
102
+ type=click.Path(exists=True, dir_okay=False, path_type=Path),
103
+ )
104
+ @click.option(
105
+ "-o",
106
+ "--output",
107
+ type=click.Path(dir_okay=False, path_type=Path),
108
+ default=None,
109
+ help="Output file. Defaults to <input>.<format>.",
110
+ )
111
+ @click.option(
112
+ "--config",
113
+ "config_path",
114
+ type=click.Path(exists=True, dir_okay=False, path_type=Path),
115
+ default=None,
116
+ help="Path to YAML config (preset + overrides). Defaults to built-in default preset.",
117
+ )
118
+ @click.option(
119
+ "--format",
120
+ "fmt",
121
+ type=click.Choice(list(_VALID_FORMATS), case_sensitive=False),
122
+ default=None,
123
+ help="Output format. Inferred from --output extension if omitted.",
124
+ )
125
+ def convert_command(
126
+ input_path: Path,
127
+ output: Path | None,
128
+ config_path: Path | None,
129
+ fmt: str | None,
130
+ ) -> None:
131
+ """Convert INPUT markdown into the chosen format."""
132
+
133
+ resolved_fmt = _resolve_format(fmt, output)
134
+ if output is None:
135
+ output = input_path.with_suffix(f".{resolved_fmt}")
136
+
137
+ def _do() -> None:
138
+ config = _load_config(config_path)
139
+ markdown = input_path.read_text(encoding="utf-8")
140
+ storage = get_storage(default_base_dir=input_path.parent)
141
+ _LOGGER.debug(
142
+ "convert input=%s output=%s format=%s preset=%s",
143
+ input_path,
144
+ output,
145
+ resolved_fmt,
146
+ config.preset,
147
+ )
148
+ data = convert_pipeline(
149
+ markdown, config, format=resolved_fmt, storage=storage
150
+ )
151
+ assert output is not None
152
+ output.write_bytes(data)
153
+ click.echo(f"Wrote {output}")
154
+
155
+ _execute(_do)
156
+
157
+
158
+ @cli.command("validate")
159
+ @click.argument(
160
+ "input_path",
161
+ metavar="INPUT",
162
+ type=click.Path(exists=True, dir_okay=False, path_type=Path),
163
+ )
164
+ @click.option(
165
+ "--config",
166
+ "config_path",
167
+ type=click.Path(exists=True, dir_okay=False, path_type=Path),
168
+ default=None,
169
+ help="Path to YAML config (preset + overrides). Defaults to built-in default preset.",
170
+ )
171
+ def validate_command(input_path: Path, config_path: Path | None) -> None:
172
+ """Validate INPUT markdown + config without producing output."""
173
+
174
+ def _do() -> None:
175
+ config = _load_config(config_path)
176
+ markdown = input_path.read_text(encoding="utf-8")
177
+ from markdown_gost.render.bibliography import BibliographyIndex
178
+ from markdown_gost.render.references import prepare_document
179
+
180
+ document = prepare_document(parse_markdown(markdown), config)
181
+ BibliographyIndex.from_document(document, config)
182
+ click.echo(f"OK (preset={config.preset})")
183
+
184
+ _execute(_do)
185
+
186
+
187
+ cli.add_command(import_command)
188
+
189
+
190
+ if __name__ == "__main__":
191
+ cli()
File without changes
@@ -0,0 +1,134 @@
1
+ """``markdown-gost import`` — конвертация DOCX/PDF → расширенный markdown.
2
+
3
+ T036 ввёл docx-импорт; T041 добавил PDF через unoserver-prepass.
4
+
5
+ Exit codes:
6
+
7
+ * ``0`` — успех (даже если были fallbacks).
8
+ * ``1`` — невалидный вход: файл не найден или расширение не ``.docx`` / ``.pdf``.
9
+ * ``2`` — pandoc/unoserver упал на верхнем уровне.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import logging
15
+ import re
16
+ import sys
17
+ from pathlib import Path
18
+
19
+ import click
20
+
21
+ from markdown_gost.import_ import ImportContext, ImportResult, import_docx, import_pdf
22
+ from markdown_gost.import_.metrics import IMPORT_TOTAL
23
+ from markdown_gost.import_.pandoc_runner import PandocError
24
+ from markdown_gost.output.pdf_writer import UnoserverError
25
+ from markdown_gost.storage import get_storage
26
+
27
+ _LOGGER = logging.getLogger("markdown_gost.cli.import")
28
+
29
+ _HEADING_RE = re.compile(r"^#{1,6}\s")
30
+ _TABLE_SEP_RE = re.compile(r"^\s*\|?\s*:?-{2,}:?\s*(\|\s*:?-{2,}:?\s*)+\|?\s*$")
31
+
32
+
33
+ def _count_headings(markdown: str) -> int:
34
+ return sum(1 for line in markdown.splitlines() if _HEADING_RE.match(line))
35
+
36
+
37
+ def _count_tables(markdown: str) -> int:
38
+ return sum(1 for line in markdown.splitlines() if _TABLE_SEP_RE.match(line))
39
+
40
+
41
+ def _total_fallbacks(result: ImportResult) -> int:
42
+ return sum(result.fallbacks.values())
43
+
44
+
45
+ @click.command("import")
46
+ @click.argument("input_path", metavar="INPUT", type=click.Path(path_type=Path))
47
+ @click.option(
48
+ "-o",
49
+ "--output",
50
+ type=click.Path(dir_okay=False, path_type=Path),
51
+ default=None,
52
+ help="Output markdown path. Defaults to <input-stem>.md рядом с input.",
53
+ )
54
+ @click.option(
55
+ "--images-dir",
56
+ "images_dir",
57
+ type=click.Path(file_okay=False, path_type=Path),
58
+ default=None,
59
+ help="Куда класть извлечённые картинки. По умолчанию <output-stem>_files/.",
60
+ )
61
+ def import_command(
62
+ input_path: Path,
63
+ output: Path | None,
64
+ images_dir: Path | None,
65
+ ) -> None:
66
+ """Импорт DOCX/PDF в расширенный markdown markdown_gost.
67
+
68
+ PDF-input идёт через unoserver-prepass (PDF → DOCX → md). Сканированные
69
+ PDF без текстового слоя возвращаются как пустой md без падения.
70
+ """
71
+ if not input_path.exists():
72
+ click.echo(f"Error: input not found: {input_path}", err=True)
73
+ sys.exit(1)
74
+
75
+ suffix = input_path.suffix.lower()
76
+ if suffix not in (".docx", ".pdf"):
77
+ click.echo(
78
+ f"Error: unsupported input extension {suffix!r}; expected .docx or .pdf",
79
+ err=True,
80
+ )
81
+ sys.exit(1)
82
+
83
+ if output is None:
84
+ output = input_path.with_suffix(".md")
85
+ if images_dir is None:
86
+ images_dir = output.with_name(f"{output.stem}_files")
87
+
88
+ storage = get_storage(default_base_dir=output.parent)
89
+ ctx = ImportContext(
90
+ storage=storage,
91
+ images_prefix=None,
92
+ images_dir=images_dir,
93
+ )
94
+ fmt_label = "pdf" if suffix == ".pdf" else "docx"
95
+
96
+ _LOGGER.info(
97
+ "import input=%s output=%s images_dir=%s format=%s",
98
+ input_path,
99
+ output,
100
+ images_dir,
101
+ fmt_label,
102
+ )
103
+
104
+ importer = import_pdf if suffix == ".pdf" else import_docx
105
+ try:
106
+ result = importer(input_path, ctx)
107
+ except (PandocError, UnoserverError) as exc:
108
+ IMPORT_TOTAL.labels(format=fmt_label, result="error").inc()
109
+ click.echo(f"Error: {exc}", err=True)
110
+ sys.exit(2)
111
+
112
+ output.parent.mkdir(parents=True, exist_ok=True)
113
+ output.write_text(result.markdown, encoding="utf-8")
114
+
115
+ if logging.getLogger().level <= logging.DEBUG and result.warnings:
116
+ for warning in result.warnings:
117
+ click.echo(f"warning: {warning}", err=True)
118
+
119
+ fallback_total = _total_fallbacks(result)
120
+ IMPORT_TOTAL.labels(format=fmt_label, result="success").inc()
121
+ if fallback_total > 0:
122
+ IMPORT_TOTAL.labels(format=fmt_label, result="fallback").inc()
123
+
124
+ click.echo(
125
+ "Imported: "
126
+ f"{_count_headings(result.markdown)} headings, "
127
+ f"{_count_tables(result.markdown)} tables, "
128
+ f"{len(result.images)} images, "
129
+ f"{fallback_total} fallbacks",
130
+ err=True,
131
+ )
132
+
133
+
134
+ __all__ = ["import_command"]
File without changes
@@ -0,0 +1,8 @@
1
+ class ConfigError(Exception):
2
+ """Base class for configuration loading errors."""
3
+
4
+
5
+ class UnknownPresetError(ConfigError):
6
+ def __init__(self, name: str) -> None:
7
+ super().__init__(f"Unknown preset: {name!r}")
8
+ self.name = name
@@ -0,0 +1,59 @@
1
+ from importlib import resources
2
+ from pathlib import Path
3
+ from typing import Any
4
+
5
+ import yaml
6
+
7
+ from markdown_gost.config.errors import UnknownPresetError
8
+ from markdown_gost.config.schema import Config
9
+
10
+
11
+ def _deep_merge(base: dict[str, Any], overrides: dict[str, Any]) -> dict[str, Any]:
12
+ result: dict[str, Any] = dict(base)
13
+ for key, value in overrides.items():
14
+ existing = result.get(key)
15
+ if isinstance(existing, dict) and isinstance(value, dict):
16
+ result[key] = _deep_merge(existing, value)
17
+ else:
18
+ result[key] = value
19
+ return result
20
+
21
+
22
+ def _load_preset(name: str) -> dict[str, Any]:
23
+ try:
24
+ text = (
25
+ resources.files("markdown_gost.config.presets")
26
+ .joinpath(f"{name}.yaml")
27
+ .read_text(encoding="utf-8")
28
+ )
29
+ except (FileNotFoundError, IsADirectoryError, ModuleNotFoundError) as e:
30
+ raise UnknownPresetError(name) from e
31
+ parsed = yaml.safe_load(text)
32
+ if parsed is None:
33
+ return {}
34
+ if not isinstance(parsed, dict):
35
+ raise UnknownPresetError(name)
36
+ return parsed
37
+
38
+
39
+ def load_config_from_string(content: str) -> Config:
40
+ user = yaml.safe_load(content)
41
+ if not isinstance(user, dict):
42
+ return Config.model_validate(user if user is not None else {})
43
+
44
+ preset_name = user.get("preset")
45
+ if not isinstance(preset_name, str) or not preset_name:
46
+ return Config.model_validate(user)
47
+
48
+ preset_data = _load_preset(preset_name)
49
+ overrides = user.get("overrides") or {}
50
+ if not isinstance(overrides, dict):
51
+ return Config.model_validate({"preset": preset_name, "overrides": overrides})
52
+
53
+ merged = _deep_merge(preset_data, overrides)
54
+ merged["preset"] = preset_name
55
+ return Config.model_validate(merged)
56
+
57
+
58
+ def load_config_from_path(path: Path) -> Config:
59
+ return load_config_from_string(path.read_text(encoding="utf-8"))
File without changes
@@ -0,0 +1,94 @@
1
+ page:
2
+ size: A4
3
+ orientation: portrait
4
+ margins:
5
+ top: 2cm
6
+ right: 1cm
7
+ bottom: 1.25cm
8
+ left: 2.5cm
9
+
10
+ font:
11
+ family: Times New Roman
12
+ size: 14pt
13
+ line_spacing: 1.5
14
+
15
+ paragraph:
16
+ alignment: justify
17
+ indent_first_line: 1.25cm
18
+
19
+ headings:
20
+ numbering: continuous
21
+ leading_space_in_numbered: true
22
+ levels:
23
+ 1:
24
+ size: 14pt
25
+ bold: true
26
+ uppercase: true
27
+ alignment: left
28
+ space_before: 0pt
29
+ space_after: 0pt
30
+ page_break_before: true
31
+ keep_with_next: true
32
+ indent_first_line: 1.25cm
33
+ 2:
34
+ size: 14pt
35
+ bold: true
36
+ alignment: left
37
+ space_before: 12pt
38
+ space_after: 0pt
39
+ keep_with_next: true
40
+ indent_first_line: 1.25cm
41
+ 3:
42
+ size: 14pt
43
+ bold: true
44
+ alignment: left
45
+ space_before: 12pt
46
+ space_after: 0pt
47
+ keep_with_next: true
48
+ indent_first_line: 1.25cm
49
+
50
+ captions:
51
+ image:
52
+ italic: false
53
+ bold: false
54
+ alignment: center
55
+ format: "{category} {number} — {text}"
56
+ space_before: 0pt
57
+ space_after: 0pt
58
+ table:
59
+ italic: false
60
+ bold: false
61
+ alignment: left
62
+ format: "{category} {number} — {text}"
63
+ space_before: 0pt
64
+ space_after: 0pt
65
+ listing:
66
+ italic: false
67
+ bold: false
68
+ alignment: left
69
+ format: "{category} {number} — {text}"
70
+ space_before: 0pt
71
+ space_after: 0pt
72
+
73
+ table:
74
+ repeat_header_on_break: true
75
+ space_before: 0pt
76
+ space_after: 12pt
77
+
78
+ listing:
79
+ syntax_highlighting: false
80
+ font:
81
+ family: Consolas
82
+ size: 12pt
83
+ line_spacing: 1.0
84
+ space_before: 0pt
85
+ space_after: 12pt
86
+
87
+ lists:
88
+ bullet_marker: "—"
89
+ indent_left: 1.25cm
90
+ indent_first_line: 0cm
91
+
92
+ equation:
93
+ numbering_alignment: right
94
+ parentheses: true
@@ -0,0 +1,124 @@
1
+ page:
2
+ size: A4
3
+ orientation: portrait
4
+ margins:
5
+ top: 20mm
6
+ right: 15mm
7
+ bottom: 20mm
8
+ left: 30mm
9
+
10
+ font:
11
+ family: Times New Roman
12
+ size: 14pt
13
+ line_spacing: 1.5
14
+
15
+ paragraph:
16
+ alignment: justify
17
+ indent_first_line: 1.25cm
18
+
19
+ headings:
20
+ numbering: continuous
21
+ leading_space_in_numbered: true
22
+ levels:
23
+ 1:
24
+ size: 14pt
25
+ bold: true
26
+ italic: false
27
+ uppercase: false
28
+ alignment: left
29
+ space_before: 0pt
30
+ space_after: 0pt
31
+ page_break_before: true
32
+ keep_with_next: true
33
+ indent_first_line: 1.25cm
34
+ 2:
35
+ size: 14pt
36
+ bold: true
37
+ italic: false
38
+ uppercase: false
39
+ alignment: left
40
+ space_before: 0pt
41
+ space_after: 0pt
42
+ page_break_before: false
43
+ keep_with_next: true
44
+ indent_first_line: 1.25cm
45
+ 3:
46
+ size: 14pt
47
+ bold: true
48
+ italic: false
49
+ uppercase: false
50
+ alignment: left
51
+ space_before: 0pt
52
+ space_after: 0pt
53
+ page_break_before: false
54
+ keep_with_next: true
55
+ indent_first_line: 1.25cm
56
+ structural:
57
+ size: 14pt
58
+ bold: true
59
+ italic: false
60
+ uppercase: true
61
+ alignment: center
62
+ space_before: 0pt
63
+ space_after: 0pt
64
+ page_break_before: true
65
+ keep_with_next: true
66
+ indent_first_line: 0cm
67
+ structural_titles:
68
+ - СОДЕРЖАНИЕ
69
+ - ВВЕДЕНИЕ
70
+ - ЗАКЛЮЧЕНИЕ
71
+ - СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
72
+
73
+ captions:
74
+ image:
75
+ italic: false
76
+ bold: false
77
+ alignment: center
78
+ format: "{category} {number} — {text}"
79
+ space_before: 0pt
80
+ space_after: 0pt
81
+ line_spacing: 1.0
82
+ table:
83
+ italic: false
84
+ bold: false
85
+ alignment: left
86
+ format: "{category} {number} — {text}"
87
+ space_before: 0pt
88
+ space_after: 0pt
89
+ line_spacing: 1.0
90
+ listing:
91
+ italic: false
92
+ bold: false
93
+ alignment: left
94
+ format: "{category} {number} — {text}"
95
+ space_before: 0pt
96
+ space_after: 0pt
97
+ line_spacing: 1.0
98
+ continuation_break: false
99
+
100
+ table:
101
+ repeat_header_on_break: true
102
+ header_bold: true
103
+ space_before: 0pt
104
+ space_after: 0pt
105
+
106
+ listing:
107
+ syntax_highlighting: false
108
+ font:
109
+ family: Consolas
110
+ size: 14pt
111
+ line_spacing: 1.0
112
+ space_before: 0pt
113
+ space_after: 0pt
114
+
115
+ lists:
116
+ bullet_marker: "—"
117
+ indent_left: 1.25cm
118
+ indent_first_line: 0cm
119
+
120
+ equation:
121
+ space_before: 21pt
122
+ space_after: 21pt
123
+ numbering_alignment: right
124
+ parentheses: true