import PyPDF2 import requests import json from reportlab.lib.pagesizes import letter from reportlab.lib.units import inch from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.lib.enums import TA_JUSTIFY from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont import os # Configuration PDF_PATH = "TaniaBorecMemoir(Ukr).pdf" # Fichier original OLLAMA_MODEL = "traductionUkrainienVersFrancais:latest" OLLAMA_URL = "http://localhost:11434/api/generate" # URL par défaut d'Ollama TARGET_LANGUAGE = "français" # Langue cible (ex: "français", "anglais", "allemand", "espagnol", etc.) OUTPUT_PDF_PATH = PDF_PATH.replace(".pdf", f" ({TARGET_LANGUAGE.upper()[:2]}).pdf") # Chemin du PDF de sortie def extract_parameters_from_template(template_str): """Extrait les paramètres du modèle à partir du template.""" import re parameters = {} if not template_str or not isinstance(template_str, str): return parameters # Si la chaîne contient "parameters:", récupère ce qui suit if 'parameters:' in template_str: params_section = template_str.split('parameters:', 1)[1] else: # Sinon, utilise la chaîne directement (elle contient déjà les paramètres) params_section = template_str # Parse les lignes de paramètres # Format: "stop """ # "temperature 0.1" lines = params_section.split('\n') for line in lines: if not line.strip(): continue # Divise par le premier groupe d'espaces blancs # Cela sépare la clé des valeurs avec leurs espaces parts = line.split(None, 1) # split() avec maxsplit=1 divise sur les espaces if len(parts) == 2: param_name = parts[0].strip() param_value = parts[1].strip() parameters[param_name] = param_value return parameters def get_llm_model_info(model=OLLAMA_MODEL): """Extrait les informations du modèle LLM depuis Ollama.""" try: # URL pour obtenir les informations du modèle info_url = OLLAMA_URL.replace("/api/generate", "/api/show") payload = {"name": model} response = requests.post(info_url, json=payload) if response.status_code == 200: model_data = response.json() # Gère le cas où model_data est une chaîne if isinstance(model_data, str): model_data = json.loads(model_data) # Extrait les paramètres du template parameters = model_data.get('parameters', '') parsed_params = extract_parameters_from_template(parameters) # Extraction des informations principales info = { "temperature": parsed_params.get('temperature', model_data.get("temperature", "Not available")), "num_ctx": parsed_params.get('num_ctx', "Not available"), "top_k": parsed_params.get('top_k', "Not available"), "top_p": parsed_params.get('top_p', "Not available"), "system": model_data.get("system", "Not available"), "modified_at": model_data.get("modified_at", "Not available"), } return info else: print(f"Erreur lors de la récupération du modèle : {response.text}") return None except Exception as e: print(f"Erreur lors de l'accès aux informations du modèle : {e}") return None def display_llm_info(): """Retourne les informations du modèle LLM formatées.""" from datetime import datetime info = get_llm_model_info(OLLAMA_MODEL) if info: # Formate la date en jj/mm/AAAA modified_at = info.get('modified_at', 'Not available') if modified_at and modified_at != 'Not available': try: # Parse la date ISO date_obj = datetime.fromisoformat(modified_at.replace('Z', '+00:00')) # Formate en jj/mm/AAAA formatted_date = date_obj.strftime("%d/%m/%Y") except: formatted_date = modified_at else: formatted_date = 'Not available' return f"LLM Modèle: {OLLAMA_MODEL}
\nSystem: {info['system']}
\nTemperature: {info['temperature']}
\nDate de modification: {formatted_date}" else: return "Informations du modèle non disponibles" def extract_text_from_pdf(pdf_path): """Extrait le texte page par page d'un PDF sans les numéros de pages.""" import re text_by_page = [] with open(pdf_path, "rb") as file: reader = PyPDF2.PdfReader(file) for page in reader.pages: text = page.extract_text() # Supprime les numéros de pages (nombres seuls en début/fin de ligne) text = re.sub(r'^\d+\s*\n', '', text, flags=re.MULTILINE) text = re.sub(r'\n\s*\d+\s*$', '', text, flags=re.MULTILINE) text_by_page.append(text) return text_by_page def merge_paragraphs_across_pages(pages_text): """ Divise le texte en paragraphes en détectant un point suivi d'un saut de ligne ou d'un retour à la ligne. Conserve les sauts de ligne à l'intérieur des paragraphes. """ import re # Concatène tout le texte full_text = "\n".join(pages_text) # Divise le texte en paragraphes : un point suivi d'un saut de ligne ou d'un retour à la ligne paragraphs = re.split(r'(?<=[.!?])\s*\n+', full_text.strip()) # Conserve les sauts de ligne à l'intérieur des paragraphes paragraphs = [p.strip() for p in paragraphs if p.strip()] return paragraphs def send_to_ollama(text, target_lang=TARGET_LANGUAGE, model=OLLAMA_MODEL, context_size=128000): """Envoie une requête à Ollama et retourne la réponse traduite.""" # Construit le prompt avec les instructions système et la demande de traduction full_prompt = f"\n\nTraduis le texte suivant de l'ukrainien vers le {target_lang} :\n{text}" payload = { "model": model, "prompt": full_prompt, "stream": False, "options": {"num_ctx": context_size} } response = requests.post(OLLAMA_URL, data=json.dumps(payload)) if response.status_code == 200: return response.json()["response"] else: raise Exception(f"Erreur Ollama: {response.text}") def register_unicode_font(): """Enregistre une police TrueType qui supporte le cyrilique.""" # Recherche une police système qui supporte le cyrilique font_paths = [ r"C:\Windows\Fonts\DejaVuSans.ttf", r"C:\Windows\Fonts\Calibri.ttf", r"C:\Windows\Fonts\arial.ttf", ] for font_path in font_paths: if os.path.exists(font_path): try: pdfmetrics.registerFont(TTFont('UnicodeFont', font_path)) return 'UnicodeFont' except Exception as e: print(f"Erreur lors de l'enregistrement de {font_path}: {e}") # Si aucune police spéciale trouvée, utilise Helvetica par défaut print("Aucune police Unicode trouvée, utilisation d'Helvetica") return 'Helvetica' def create_pdf_from_results(results, output_path): """Crée un PDF à partir des résultats de traduction.""" doc = SimpleDocTemplate(output_path, pagesize=letter, topMargin=inch, bottomMargin=inch) story = [] # Enregistre une police qui supporte le cyrilique font_name = register_unicode_font() # Style personnalisé styles = getSampleStyleSheet() title_style = ParagraphStyle( 'CustomTitle', parent=styles['Heading1'], fontSize=16, textColor='#1f4788', spaceAfter=0.3*inch, alignment=TA_JUSTIFY, fontName=font_name ) page_style = ParagraphStyle( 'PageHeading', parent=styles['Heading2'], fontSize=12, textColor='#1f4788', spaceAfter=0.2*inch, spaceBefore=0.2*inch, fontName=font_name ) body_style = ParagraphStyle( 'CustomBody', parent=styles['BodyText'], fontSize=11, alignment=TA_JUSTIFY, spaceAfter=0.2*inch, fontName=font_name ) # Titre avec la langue cible title_text = f"Traduction - Ukrainien vers {TARGET_LANGUAGE.capitalize()}" story.append(Paragraph(title_text, title_style)) story.append(Spacer(1, 0.2*inch)) # Contenu for page_num, translation in results.items(): # Préserver la mise en page en convertissant les sauts de ligne formatted_text = translation.replace("\n", "
") story.append(Paragraph(formatted_text, body_style)) story.append(Spacer(1, 0.1*inch)) # Infos sur le LLM story.append(Spacer(1, 0.2*inch)) story.append(Paragraph(display_llm_info(), page_style)) # Construction du PDF doc.build(story) print(f"PDF généré avec succès : {output_path}") def main(): # Affiche les informations du modèle LLM display_llm_info() # Extraction du texte page par page pages = extract_text_from_pdf(PDF_PATH) print(f"Nombre de pages extraites : {len(pages)}") # Fusion des paragraphes qui s'étendent sur plusieurs pages paragraphs = merge_paragraphs_across_pages(pages) print(f"Nombre de paragraphes complets extraits : {len(paragraphs)}") # Dictionnaire pour stocker les résultats results = {} # Traitement des paragraphes complets for i, paragraph_text in enumerate(paragraphs, start=1): if( i > 8 ): break print(f"{15 * '-'} Traduction du paragraphe {i}/{len(paragraphs)}...") try: result = send_to_ollama(paragraph_text, target_lang=TARGET_LANGUAGE) print(f"{result}.") results[i] = result except Exception as e: print(f"Erreur lors du traitement du paragraphe {i} : {e}") results[i] = f"Erreur lors du traitement du paragraphe {i} : {e}" # Création du PDF avec tous les résultats create_pdf_from_results(results, OUTPUT_PDF_PATH) if __name__ == "__main__": main()