BigUpdate
Python application / build (push) Has been cancelled

This commit is contained in:
Igor20264
2026-09-03 10:44:08 +03:00
parent d2da20fdb2
commit 516abe7b83
177 changed files with 40178 additions and 2 deletions
+156
View File
@@ -0,0 +1,156 @@
from copy import copy
import os
from typing import Generator, Callable
from docx.oxml import CT_Tbl
from docx.shared import Length, Pt, RGBColor, Twips
from docx.table import Table
from pygments import highlight
from pygments.formatter import Formatter
from pygments.lexers import get_lexer_by_name
from .caption import Caption, CaptionInfo
from .paragraph import Paragraph
from .renderable import Renderable
from .requires_numbering import RequiresNumbering
from ..docx_elements import create_table, _twips
from ..layout_tracker import LayoutState
from ..rendered_info import RenderedInfo
from ..sub_renderable import SubRenderable
class DocxParagraphPygmentsFormatter(Formatter):
def __init__(self, paragraphs: list[Paragraph], creator: Callable[[], Paragraph], **options):
Formatter.__init__(self, style="sas", **options)
self._creator = creator
self._paragraphs = paragraphs
self._styles = {}
for token, style in self.style:
self._styles[token] = style
def _add_run_to_last_paragraph(self, text, style):
self._paragraphs[-1].add_run(text, style["bold"] or None, style["italic"] in style or None,
RGBColor.from_string(style['color']) if style['color'] else None)
def format(self, tokensource, outfile):
self._paragraphs.append(self._creator())
for ttype, value in tokensource:
style = self._styles[ttype]
lines = iter(value.split("\n"))
self._add_run_to_last_paragraph(next(lines), style)
for line in lines:
self._paragraphs.append(self._creator())
self._add_run_to_last_paragraph(line, style)
self._paragraphs.pop(-1) # remove last empty line
LISTING_OFFSET = Pt(31) - Twips(108 * 2) # todo: fix
class Listing(Renderable, RequiresNumbering):
def __init__(self, parent, language: str, caption_info: CaptionInfo):
super().__init__("Листинг")
self._caption_info = caption_info
self._language = language
self._parent = parent
self.paragraphs: list[Paragraph] = []
self._number = None
if caption_info and caption_info.unique_name:
self.unique_name = caption_info.unique_name
def _create_table(self, parent, width: Length):
# todo: style inheritance
left_margin = Twips(int(
parent.part.styles["Normal Table"]._element.xpath("w:tblPr/w:tblCellMar/w:left")[0].attrib[
"{http://schemas.openxmlformats.org/wordprocessingml/2006/main}w"]))
right_margin = Twips(int(
parent.part.styles["Normal Table"]._element.xpath("w:tblPr/w:tblCellMar/w:right")[0].attrib[
"{http://schemas.openxmlformats.org/wordprocessingml/2006/main}w"]))
return create_table(
parent, 1, 1,
Twips(_twips(width) + _twips(left_margin) + _twips(right_margin)),
)
def set_text(self, text: str):
def create_paragraph() -> Paragraph:
paragraph = Paragraph(self._parent)
paragraph.style = "Code"
return paragraph
text = text.removesuffix("\n")
if self._language and "SYNTAX_HIGHLIGHTING" in os.environ and os.environ["SYNTAX_HIGHLIGHTING"] == "1":
formatter = DocxParagraphPygmentsFormatter(self.paragraphs, lambda: create_paragraph())
highlight(text, get_lexer_by_name(self._language), formatter)
else:
for line in text.removesuffix("\n").split("\n"):
paragraph = create_paragraph()
paragraph.add_run(line)
self.paragraphs.append(paragraph)
def set_number(self, number: str):
self._number = number
def render(self, previous_rendered: RenderedInfo, layout_state: LayoutState)\
-> Generator[RenderedInfo | SubRenderable, None, None]:
caption_rendered_infos = list(
Caption(self._parent, "Листинг", self._caption_info, self._number, True)
.render(previous_rendered, copy(layout_state))
)
layout_state.add_height(sum([info.height for info in caption_rendered_infos]))
yield from caption_rendered_infos
table = self._create_table(self._parent, layout_state.max_width)
previous = None
table_height = Pt(1) # table borders, 4 eights of point for each border
# if first line doesn't fit move listing to the next page
paragraph_layout_state = copy(layout_state)
paragraph_layout_state.max_width -= LISTING_OFFSET
paragraph_rendered_info = next(self.paragraphs[0].render(previous, paragraph_layout_state))
if paragraph_rendered_info.height + table_height > layout_state.remaining_page_height:
table_height += layout_state.remaining_page_height
layout_state.add_height(layout_state.remaining_page_height)
for paragraph in self.paragraphs:
paragraph_layout_state = copy(layout_state)
paragraph_layout_state.max_width -= LISTING_OFFSET
paragraph_rendered_info = next(paragraph.render(previous, paragraph_layout_state))
if paragraph_rendered_info.height > layout_state.remaining_page_height: # todo add before after
table_rendered_info = RenderedInfo(table, table_height)
yield table_rendered_info
table_height = Pt(1) # table borders, 4 eights of point for each border
continuation_paragraph = Paragraph(self._parent)
continuation_paragraph.add_run(f"Продолжение Листинга {self._number}")
continuation_paragraph.style = "Caption Listing"
continuation_paragraph.first_line_indent = 0
continuation_paragraph.page_break_before = True
continuation_rendered_info = next(
continuation_paragraph.render(None, copy(layout_state)))
layout_state.add_height(continuation_rendered_info.height)
yield continuation_rendered_info
table = self._create_table(self._parent, layout_state.max_width)
previous = None
paragraph_layout_state = copy(layout_state)
paragraph_layout_state.max_width -= LISTING_OFFSET
paragraph_rendered_info = next(paragraph.render(previous, paragraph_layout_state))
table._cells[0]._element.append(paragraph_rendered_info.docx_element._element)
layout_state.add_height(paragraph_rendered_info.height)
table_height += paragraph_rendered_info.height
previous = paragraph_rendered_info
yield RenderedInfo(table, table_height)