302 lines
11 KiB
Python
Executable File
302 lines
11 KiB
Python
Executable File
import os
|
|
import shutil
|
|
import re
|
|
import yaml
|
|
import qrcode
|
|
from qrcode.image.styledpil import StyledPilImage
|
|
from qrcode.image.styles.moduledrawers import RoundedModuleDrawer, GappedSquareModuleDrawer
|
|
from qrcode.image.styles.colormasks import SolidFillColorMask
|
|
import PIL
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
# ============================================ FUNÇÕES ==================================================
|
|
def extract_qrcode_info(filename, quantidade, pasta, config):
|
|
|
|
cor = config['cor']
|
|
site_de_publicacao = config['site_de_publicacao']
|
|
livro_url = config['livro_url']
|
|
|
|
ark = None
|
|
|
|
if 'ark' in config:
|
|
ark = 'https://n2t.net/'+config['ark']
|
|
|
|
with open(filename, 'r', encoding='utf-8') as file:
|
|
markdown_text = file.read()
|
|
|
|
# Definindo o padrão de expressão regular para capturar o conteúdo entre ::: { e }
|
|
pattern = r':::\s*\{([^{}]*)\}\s*(.*?)\s*:::'
|
|
matches = re.findall(pattern, markdown_text, re.DOTALL)
|
|
|
|
# Loop pelas correspondências encontradas
|
|
for match in matches:
|
|
div_attributes = match[0].strip()
|
|
content = match[1].strip()
|
|
|
|
|
|
# Verifica se a div tem a classe .qrcode
|
|
if '.qrcode' in div_attributes:
|
|
|
|
quantidade += 1
|
|
|
|
print("------------ INICIO -------------", quantidade)
|
|
|
|
print("Atributos:", div_attributes)
|
|
print("Conteúdo:", content)
|
|
|
|
# Verifica se a div tem a classe .content-visible
|
|
content_visible = '.content-visible' in div_attributes
|
|
|
|
if content_visible:
|
|
print("Tem a classe .content-visible")
|
|
# Extrai o valor do atributo when-format
|
|
when_format_match = re.search(r'when-format="([^"]+)"', div_attributes)
|
|
print("whenformat: ", when_format_match)
|
|
|
|
if when_format_match:
|
|
start_index = when_format_match.start()
|
|
end_index = when_format_match.end()
|
|
when_format_text = div_attributes[start_index:end_index]
|
|
print("FORMA: ",when_format_text)
|
|
|
|
else:
|
|
print("Não tem a classe .content-visible")
|
|
|
|
qrcor_attr = re.search(r'qrcor="([^"]+)"', div_attributes)
|
|
|
|
if qrcor_attr:
|
|
start_index = qrcor_attr.start()
|
|
end_index = qrcor_attr.end()
|
|
qrcor_text = div_attributes[start_index:end_index]
|
|
|
|
padrao = r'"(.*?)"'
|
|
resultados = re.findall(padrao, qrcor_text)
|
|
|
|
print("RESULTADOS: ",resultados)
|
|
|
|
if resultados[0] == "0":
|
|
qrcor = False
|
|
else:
|
|
qrcor = True
|
|
|
|
else:
|
|
qrcor = True
|
|
|
|
# ================================ GERAR QRCODE ================================
|
|
|
|
if not hasattr(PIL.Image, 'Resampling'):
|
|
PIL.Image.Resampling = PIL.Image
|
|
|
|
hex_color = cor
|
|
rgb_color = hex_to_rgb(hex_color)
|
|
|
|
|
|
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_H)
|
|
|
|
# ++++++++++++++++++++ DEFININDO OS LINKS DOS QR CODES +++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
# define qual será a base para os links internos no qrcode +
|
|
if ark is not None:
|
|
base = ark
|
|
else:
|
|
base = site_de_publicacao+'/'+livro_url
|
|
|
|
# verifica se o link é externo ou interno
|
|
if not 'http' in content and '#' in content:
|
|
|
|
#link interno
|
|
|
|
# Verifica se é para o um capítulo diferente do avaliado. Se tiver a barra, entao é para outro capítulo
|
|
|
|
if '/' in content:
|
|
content = base +'/'+ content
|
|
else:
|
|
cap = filename.split('.')[0]
|
|
content = base +'/'+ cap +'/'+ content
|
|
|
|
|
|
qr.add_data(content)
|
|
|
|
qr_inner_eyes_img = qr.make_image(image_factory=StyledPilImage,
|
|
eye_drawer=RoundedModuleDrawer(radius_ratio=1),
|
|
color_mask=SolidFillColorMask(front_color=(rgb_color['red'], rgb_color['green'], rgb_color['blue'])))
|
|
|
|
qr_outer_eyes_img = qr.make_image(image_factory=StyledPilImage,
|
|
eye_drawer=RoundedModuleDrawer(radius_ratio=1))
|
|
|
|
qr_img = qr.make_image(image_factory=StyledPilImage,
|
|
module_drawer=GappedSquareModuleDrawer(),
|
|
embeded_image_path="editora/logo_roxo.png")
|
|
|
|
inner_eye_mask = style_inner_eyes(qr_img)
|
|
outer_eye_mask = style_outer_eyes(qr_img)
|
|
intermediate_img = Image.composite(qr_inner_eyes_img, qr_img, inner_eye_mask)
|
|
final_image = Image.composite(qr_outer_eyes_img, intermediate_img, outer_eye_mask)
|
|
|
|
final_image = final_image.convert('CMYK')
|
|
|
|
# Verifica se tem que ser preto e branco
|
|
if not qrcor:
|
|
final_image = final_image.convert('L')
|
|
final_image = final_image.convert('CMYK')
|
|
|
|
# Separa os canais CMYK
|
|
c, m, y, k = final_image.split()
|
|
|
|
# Cria novos canais C, M e Y com todos os valores definidos como zero
|
|
c_new = c.point(lambda x: 0)
|
|
m_new = m.point(lambda x: 0)
|
|
y_new = y.point(lambda x: 0)
|
|
|
|
# Cria uma nova imagem CMYK com os canais ajustados e o canal K original
|
|
final_image = Image.merge('CMYK', (c_new, m_new, y_new, k))
|
|
|
|
arq = "qrcode_" + str(quantidade) + ".jpg"
|
|
caminho_e_arq = os.path.join(pasta, arq)
|
|
final_image.save(caminho_e_arq, quality=100)
|
|
|
|
|
|
|
|
print("QRCOR: ",qrcor)
|
|
|
|
print("------------ FIM -------------")
|
|
|
|
return quantidade
|
|
|
|
|
|
def obter_capitulos(arquivo_yaml):
|
|
|
|
with open(arquivo_yaml, 'r') as file:
|
|
# Carrega o conteúdo do arquivo YAML
|
|
conteudo = yaml.safe_load(file)
|
|
# Verifica se a chave 'capítulos' está presente no YAML
|
|
if 'capitulos' in conteudo:
|
|
return conteudo['capitulos']
|
|
else:
|
|
print("A chave 'capítulos' não foi encontrada no YAML.")
|
|
return None
|
|
|
|
# A ideia aqui é obter as configuracoes do qrcode bem como o identificador ark e o site para publicacao
|
|
def obter_metadados(arquivo_yaml):
|
|
|
|
metadados = {}
|
|
|
|
with open(arquivo_yaml, 'r') as file:
|
|
# Carrega o conteúdo do arquivo YAML
|
|
conteudo = yaml.safe_load(file)
|
|
|
|
# Verifica se a chave 'capítulos' está presente no YAML
|
|
if 'qrcode' in conteudo:
|
|
metadados['cor'] = conteudo['qrcode']['cor']
|
|
|
|
if 'site-de-publicacao' in conteudo:
|
|
metadados['site_de_publicacao'] = conteudo['site-de-publicacao']
|
|
|
|
metadados['livro_url'] = conteudo['pasta-livro-finalizado']['html']
|
|
|
|
with open('_quarto-html.yml', 'r') as file:
|
|
|
|
conteudo_1 = yaml.safe_load(file)
|
|
|
|
if 'ark' in conteudo_1['moan-dados']:
|
|
metadados['ark'] = conteudo_1['moan-dados']['ark']
|
|
|
|
|
|
return metadados
|
|
|
|
|
|
|
|
|
|
def criar_ou_limpar_pasta(nome_pasta):
|
|
# Verifica se o diretório já existe
|
|
if os.path.exists(nome_pasta):
|
|
# Remove todo o conteúdo da pasta
|
|
for filename in os.listdir(nome_pasta):
|
|
file_path = os.path.join(nome_pasta, filename)
|
|
try:
|
|
if os.path.isfile(file_path) or os.path.islink(file_path):
|
|
os.unlink(file_path)
|
|
elif os.path.isdir(file_path):
|
|
shutil.rmtree(file_path)
|
|
except Exception as e:
|
|
print(f"Erro ao remover {file_path}: {e}")
|
|
else:
|
|
# Cria a pasta se não existir
|
|
try:
|
|
os.makedirs(nome_pasta)
|
|
except OSError as e:
|
|
print(f"Erro ao criar a pasta {nome_pasta}: {e}")
|
|
|
|
|
|
# ============================================ FUNÇÕES QRCODE ==================================================
|
|
|
|
|
|
def hex_to_rgb(hex_color):
|
|
# Remover o '#' se presente
|
|
hex_color = hex_color.lstrip('#')
|
|
|
|
# Converter para valores RGB
|
|
red = int(hex_color[0:2], 16)
|
|
green = int(hex_color[2:4], 16)
|
|
blue = int(hex_color[4:6], 16)
|
|
|
|
# Retornar como um dicionário
|
|
return {'red': red, 'green': green, 'blue': blue}
|
|
|
|
# Custom function for eye styling. These create the eye masks
|
|
def style_inner_eyes(img):
|
|
img_size = img.size[0]
|
|
eye_size = 70 # default
|
|
quiet_zone = 40 # default
|
|
mask = Image.new('L', img.size, 0)
|
|
draw = ImageDraw.Draw(mask)
|
|
draw.rectangle((60, 60, 90, 90), fill=255) #top left eye
|
|
draw.rectangle((img_size-90, 60, img_size-60, 90), fill=255) #top right eye
|
|
draw.rectangle((60, img_size-90, 90, img_size-60), fill=255) #bottom left eye
|
|
return mask
|
|
|
|
def style_outer_eyes(img):
|
|
img_size = img.size[0]
|
|
eye_size = 70 # default
|
|
quiet_zone = 40 # default
|
|
mask = Image.new('L', img.size, 0)
|
|
draw = ImageDraw.Draw(mask)
|
|
draw.rectangle((40, 40, 110, 110), fill=255) #top left eye
|
|
draw.rectangle((img_size-110, 40, img_size-40, 110), fill=255) #top right eye
|
|
draw.rectangle((40, img_size-110, 110, img_size-40), fill=255) #bottom left eye
|
|
draw.rectangle((60, 60, 90, 90), fill=0) #top left eye
|
|
draw.rectangle((img_size-90, 60, img_size-60, 90), fill=0) #top right eye
|
|
draw.rectangle((60, img_size-90, 90, img_size-60), fill=0) #bottom left eye
|
|
return mask
|
|
# ============================================ FIM DAS FUNÇÕES QRCODE ==================================================
|
|
|
|
|
|
|
|
# ============================================ FIM DAS FUNÇÕES GERAIS==================================================
|
|
|
|
|
|
contador = 0
|
|
|
|
# Caminho para o arquivo YAML
|
|
caminho_arquivo = '_moan-config.yml'
|
|
|
|
# Obtém os capítulos do arquivo YAML
|
|
capitulos = obter_capitulos(caminho_arquivo)
|
|
|
|
# metadados qrcode
|
|
config = obter_metadados(caminho_arquivo)
|
|
|
|
pasta_qr = "qr_code"
|
|
criar_ou_limpar_pasta(pasta_qr)
|
|
|
|
if capitulos:
|
|
for capitulo in capitulos:
|
|
# Usando a função para extrair informações de um arquivo .qmd
|
|
contador = extract_qrcode_info(capitulo, contador, pasta_qr,config)
|
|
|
|
else:
|
|
print("Nenhum capítulo encontrado no arquivo YAML.")
|
|
|
|
|
|
# Aguardar entrada do usuário antes de fechar o terminal
|
|
input("Pressione Enter para fechar o terminal...") |