Spaces:
Sleeping
Sleeping
| """ | |
| Formatadores de tabelas. | |
| """ | |
| from docx import Document | |
| from docx.shared import Pt, Inches, Cm | |
| from docx.enum.text import WD_ALIGN_PARAGRAPH | |
| from docx.enum.table import WD_TABLE_ALIGNMENT, WD_CELL_VERTICAL_ALIGNMENT | |
| from docx.oxml.ns import qn | |
| from docx.oxml import OxmlElement | |
| from typing import List, Optional, Union, Dict, Any | |
| from .paragraph import aplicar_cor_run | |
| WD_ALIGN_VERTICAL = WD_CELL_VERTICAL_ALIGNMENT | |
| def set_cell_shading(cell, color: str) -> None: | |
| """ | |
| Define a cor de fundo de uma célula. | |
| Args: | |
| cell: Célula da tabela | |
| color: Cor em hex (ex: "E6E6E6") | |
| """ | |
| shading = OxmlElement('w:shd') | |
| shading.set(qn('w:fill'), color) | |
| cell._tc.get_or_add_tcPr().append(shading) | |
| def set_cell_text_rotation(cell, direcao: str = 'btLr') -> None: | |
| """ | |
| Define a direção/rotação do texto em uma célula. | |
| Args: | |
| cell: Célula da tabela | |
| direcao: Direção do texto: | |
| - 'btLr': bottom-to-top, left-to-right (90° - vertical) | |
| - 'tbRl': top-to-bottom, right-to-left (270°) | |
| """ | |
| tcPr = cell._tc.get_or_add_tcPr() | |
| textDirection = OxmlElement('w:textDirection') | |
| textDirection.set(qn('w:val'), direcao) | |
| tcPr.append(textDirection) | |
| def formatar_celula_tabela(cell, texto: str, negrito: bool = False, cor=None, | |
| shading_color: Optional[str] = None, rotacao: bool = False): | |
| """ | |
| Formata uma célula de tabela com configurações padrão. | |
| Args: | |
| cell: Célula da tabela | |
| texto: Texto a inserir | |
| negrito: Se o texto deve ser negrito | |
| cor: Cor do texto (tuple ou RGBColor) | |
| shading_color: Cor de fundo da célula (hex string) | |
| rotacao: Se True, rotaciona o texto 90° (vertical) | |
| Returns: | |
| Run criado | |
| """ | |
| p = cell.paragraphs[0] | |
| p.alignment = WD_ALIGN_PARAGRAPH.CENTER | |
| p.paragraph_format.left_indent = Cm(0) | |
| p.paragraph_format.right_indent = Cm(0) | |
| p.paragraph_format.first_line_indent = Cm(0) | |
| p.paragraph_format.space_before = Pt(0) | |
| p.paragraph_format.space_after = Pt(0) | |
| cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER | |
| run = p.add_run(texto) | |
| run.font.size = Pt(9) | |
| run.font.name = 'Arial' | |
| run.bold = negrito | |
| if cor: | |
| aplicar_cor_run(run, cor) | |
| if shading_color: | |
| set_cell_shading(cell, shading_color) | |
| if rotacao: | |
| set_cell_text_rotation(cell) | |
| return run | |
| def add_simple_table( | |
| doc: Document, | |
| dados_tabela: List[List[Union[str, Dict[str, Any]]]], | |
| header_row: bool = True, | |
| largura_colunas: Optional[List[int]] = None, | |
| rotacao_cabecalho: bool = False | |
| ): | |
| """ | |
| Adiciona uma tabela simples ao documento. | |
| Args: | |
| doc: Documento | |
| dados_tabela: Lista de listas. Cada célula pode ser string ou dict {'texto', 'cor'} | |
| header_row: Se True, primeira linha é cabeçalho (negrito com fundo cinza) | |
| largura_colunas: Lista de inteiros representando proporção da largura de cada coluna. | |
| rotacao_cabecalho: Se True, rotaciona o texto dos cabeçalhos 90° (vertical) | |
| Returns: | |
| Tabela criada ou None se dados vazios | |
| """ | |
| if not dados_tabela or not dados_tabela[0]: | |
| return None | |
| num_colunas = len(dados_tabela[0]) | |
| if largura_colunas is None: | |
| largura_colunas = [1] * num_colunas | |
| elif len(largura_colunas) != num_colunas: | |
| raise ValueError("largura_colunas deve ter o mesmo número de colunas de dados_tabela") | |
| total = sum(largura_colunas) | |
| proporcoes = [x / total for x in largura_colunas] | |
| table = doc.add_table(rows=len(dados_tabela), cols=num_colunas) | |
| table.style = 'Table Grid' | |
| table.alignment = WD_TABLE_ALIGNMENT.CENTER | |
| largura_total = Inches(6.5) | |
| larguras_em_colunas = [largura_total * p for p in proporcoes] | |
| for i, row_data in enumerate(dados_tabela): | |
| row = table.rows[i] | |
| for j, cell_data in enumerate(row_data): | |
| if j >= len(row.cells): | |
| continue | |
| cell = row.cells[j] | |
| if isinstance(cell_data, dict): | |
| texto = cell_data.get('texto', '') | |
| cor = cell_data.get('cor') | |
| else: | |
| texto = str(cell_data) if cell_data else "" | |
| cor = None | |
| is_header = header_row and i == 0 | |
| formatar_celula_tabela( | |
| cell, texto, | |
| negrito=is_header, | |
| cor=cor, | |
| shading_color="E6E6E6" if is_header else None, | |
| rotacao=is_header and rotacao_cabecalho | |
| ) | |
| cell.width = larguras_em_colunas[j] | |
| # Ajustar altura do cabeçalho para texto rotacionado | |
| if header_row and rotacao_cabecalho and dados_tabela: | |
| # Calcular o maior texto do cabeçalho | |
| header_texts = [] | |
| for cell_data in dados_tabela[0]: | |
| if isinstance(cell_data, dict): | |
| header_texts.append(cell_data.get('texto', '')) | |
| else: | |
| header_texts.append(str(cell_data) if cell_data else "") | |
| if header_texts: | |
| max_len = max(len(t) for t in header_texts) | |
| # Aproximadamente 0.18cm por caractere em Arial 9pt + margem | |
| altura_cm = max(1.0, max_len * 0.18 + 0.3) | |
| configurar_linha_tabela_altura(table.rows[0], altura_cm) | |
| doc.add_paragraph() | |
| return table | |
| def configurar_linha_tabela_altura(row, altura_cm: float = 0.6) -> None: | |
| """ | |
| Configura altura mínima de uma linha de tabela. | |
| Args: | |
| row: Linha da tabela | |
| altura_cm: Altura em centímetros | |
| """ | |
| tr = row._tr | |
| trPr = tr.get_or_add_trPr() | |
| trHeight = OxmlElement('w:trHeight') | |
| trHeight.set(qn('w:val'), str(int(Cm(altura_cm).twips))) | |
| trHeight.set(qn('w:hRule'), 'atLeast') | |
| trPr.append(trHeight) | |
| def criar_celula_cabecalho_tabela(cell, texto: str, recuo_primeira_linha_cm: float = 1.0) -> None: | |
| """ | |
| Formata uma célula de cabeçalho de seção na tabela principal. | |
| Args: | |
| cell: Célula da tabela | |
| texto: Texto do cabeçalho | |
| recuo_primeira_linha_cm: Recuo da primeira linha | |
| """ | |
| cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER | |
| p = cell.paragraphs[0] | |
| p.paragraph_format.left_indent = Cm(0) | |
| p.paragraph_format.first_line_indent = Cm(recuo_primeira_linha_cm) | |
| p.alignment = WD_ALIGN_PARAGRAPH.LEFT | |
| run = p.add_run(texto) | |
| run.bold = True | |
| run.underline = True | |
| run.font.size = Pt(11) | |
| run.font.name = 'Arial' | |
| set_cell_shading(cell, "E6E6E6") | |
| def criar_celula_dados_tabela(cell, texto: str, is_label: bool = False) -> None: | |
| """ | |
| Formata uma célula de dados na tabela principal. | |
| Args: | |
| cell: Célula da tabela | |
| texto: Texto da célula | |
| is_label: Se True, formata como rótulo (negrito) | |
| """ | |
| cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER | |
| p = cell.paragraphs[0] | |
| p.paragraph_format.left_indent = Cm(0) | |
| p.paragraph_format.first_line_indent = Cm(0) | |
| p.paragraph_format.space_before = Pt(0) | |
| p.paragraph_format.space_after = Pt(0) | |
| run = p.add_run(texto) | |
| run.bold = is_label | |
| run.font.size = Pt(9) | |
| run.font.name = 'Arial' | |