saas_api/actions/data/text.py

141 lines
5.7 KiB
Python

# Importa a biblioteca nativa 'zlib' usada para descompressão de dados binários.
import base64
import zlib
# Importa a função 'rtf_to_text' da biblioteca 'striprtf',
# responsável por converter documentos RTF em texto plano legível.
from striprtf.striprtf import rtf_to_text
# Define uma classe utilitária chamada 'String', contendo apenas métodos estáticos.
# Essa abordagem permite o uso direto sem necessidade de instanciar a classe.
class Text:
@staticmethod
def decompress(vf_string):
"""
Descomprime e decodifica texto de origem WPTools/Firebird.
Finalidade:
Converter o conteúdo de campos BLOB ou strings compactadas (como no Delphi)
em texto legível, detectando automaticamente se o conteúdo está:
- Compactado com zlib
- Codificado em ISO-8859-1 (padrão ANSI)
- Representado como bytes puros
"""
# Verifica se o valor recebido é nulo, vazio ou None.
# Se for, retorna string vazia para evitar erros de processamento.
if not vf_string:
return ""
# Caso seja um objeto tipo stream (ex: campo BLOB do Firebird)
# Campos BLOB geralmente possuem o método `.read()` para leitura de bytes.
if hasattr(vf_string, "read"):
vf_string = vf_string.read() # Lê o conteúdo completo do stream
# Garante que o valor trabalhado é uma sequência de bytes (não string)
# Se o dado já for texto (str), converte para bytes em codificação Latin-1,
# que é compatível com ISO-8859-1 usado por sistemas Delphi/Firebird.
if isinstance(vf_string, str):
vf_bytes = vf_string.encode("latin1", errors="ignore")
else:
vf_bytes = vf_string # Já está em bytes, então apenas reaproveita
# Detecta se o conteúdo foi compactado com zlib.
# A assinatura padrão do formato zlib começa com bytes: 0x78 0x9C ou 0x78 0xDA.
is_zlib = (
len(vf_bytes) > 2 and vf_bytes[0] == 0x78 and vf_bytes[1] in (0x9C, 0xDA)
)
# Se a detecção confirmar que o conteúdo é zlib, tenta descompactar.
if is_zlib:
try:
# Descompacta os bytes e decodifica o texto usando ISO-8859-1 (ANSI),
# que preserva corretamente acentuação e caracteres especiais.
text = zlib.decompress(vf_bytes).decode("iso-8859-1", errors="ignore")
return text
except Exception:
# Caso falhe (por dados corrompidos ou não comprimidos de fato),
# o fluxo continua normalmente sem interromper a execução.
pass
# Se não for zlib, tenta tratar o conteúdo como texto puro (não compactado)
try:
# Decodifica os bytes diretamente de ISO-8859-1 (padrão usado pelo Delphi)
return vf_bytes.decode("iso-8859-1", errors="ignore")
except Exception:
# Como fallback, converte para string bruta para evitar falhas.
return str(vf_string)
# >>> NOVO MÉTODO <<<
@staticmethod
def compress(text, *, encoding: str = "iso-8859-1", as_base64: bool = True):
"""
Comprime texto/dados com zlib.
Parâmetros:
text: str | bytes | stream (com .read())
encoding: encoding usado quando 'text' for str (padrão: ISO-8859-1)
as_base64: se True, retorna string Base64 do conteúdo comprimido;
caso False, retorna bytes comprimidos.
Retorno:
- bytes (zlib) quando as_base64=False
- str (Base64) quando as_base64=True
Observações:
- Use o mesmo 'encoding' ao descomprimir para simetria.
- Ideal para armazenar em BLOB ou trafegar seguro (Base64).
"""
if text is None or text == "":
return "" if as_base64 else b""
# Se for stream (ex.: BLOB do Firebird)
if hasattr(text, "read"):
raw = text.read()
else:
raw = text
# Garante bytes
if isinstance(raw, str):
raw_bytes = raw.encode(encoding, errors="ignore")
else:
raw_bytes = bytes(raw)
# Comprime com zlib
comp = zlib.compress(raw_bytes)
# Opcional: codifica em Base64 para transporte/JSON
if as_base64:
return base64.b64encode(comp).decode("ascii")
return comp
@staticmethod
def to_text(raw_text: str) -> str:
"""
Converte o conteúdo RTF em texto simples e retorna como string.
Finalidade:
- Detectar automaticamente se o conteúdo está em formato RTF.
- Converter para texto plano usando a função 'rtf_to_text' (da striprtf).
- Retornar uma string limpa e pronta para ser usada em APIs, logs, etc.
"""
# Verifica se o texto recebido está vazio ou None — retorna vazio se sim.
if not raw_text:
return ""
# Verifica se o texto começa com o cabeçalho padrão de arquivos RTF.
# Exemplo: "{\\rtf1\\ansi..." indica conteúdo em formato RTF.
if raw_text.strip().startswith("{\\rtf"):
try:
# Converte o RTF em texto simples, preservando acentuação e quebras de linha.
text = rtf_to_text(raw_text)
# Remove espaços em branco extras nas extremidades.
return text.strip()
except Exception:
# Se ocorrer erro na conversão (ex: RTF inválido),
# retorna o conteúdo original sem alterações.
return raw_text
# Caso o texto não seja RTF, apenas remove espaços em branco extras e retorna.
return raw_text.strip()