@@ -497,12 +497,147 @@ def write_docx(
497497 rtf_path .write_text (rtf_code , encoding = "utf-8" )
498498
499499 with tempfile .TemporaryDirectory () as convert_tmpdir :
500- docx_path = converter .convert (
500+ converted = converter .convert (
501501 input_files = rtf_path ,
502502 output_dir = Path (convert_tmpdir ),
503503 format = "docx" ,
504504 overwrite = True ,
505505 )
506+ if not isinstance (converted , Path ):
507+ raise TypeError (
508+ "LibreOffice conversion returned an unexpected output for a "
509+ "single input file; expected `Path`, got object of type "
510+ f"{ type (converted )!r} with value { converted !r} ."
511+ )
512+ docx_path = converted
506513 shutil .move (str (docx_path ), target_path )
507514
508515 print (target_path )
516+
517+ def write_html (
518+ self ,
519+ file_path : str | Path ,
520+ * ,
521+ converter : LibreOfficeConverter | None = None ,
522+ ) -> None :
523+ """Write the document as an HTML file.
524+
525+ Writes the document to a temporary RTF file first, and then converts
526+ it to HTML with LibreOffice. Temporary directories are used for
527+ all intermediate files to avoid placing artifacts alongside the
528+ requested output path.
529+
530+ Args:
531+ file_path: Destination path for the HTML file.
532+ Accepts string or Path input. Can be absolute or relative.
533+ Directories are created if they do not already exist.
534+ converter: Optional LibreOffice converter instance.
535+ Pass a configured instance (for example with a custom
536+ `executable_path`) to control how LibreOffice is invoked and to
537+ avoid re-initializing and re-verifying the executable path across
538+ multiple conversions. Note that each call to ``convert()`` still
539+ starts a new LibreOffice process in headless mode; the process is
540+ not kept alive between conversions.
541+
542+ Examples:
543+ ```python
544+ doc = RTFDocument(df=data, rtf_title=RTFTitle(text="Report"))
545+ doc.write_html("output/report.html")
546+ ```
547+
548+ Note:
549+ LibreOffice may create a companion directory (for example
550+ `report.html_files`) for embedded resources. When present, it is moved
551+ alongside the requested output path.
552+ """
553+ target_path = Path (file_path ).expanduser ()
554+ target_path .parent .mkdir (parents = True , exist_ok = True )
555+
556+ if converter is None :
557+ converter = LibreOfficeConverter ()
558+ with tempfile .TemporaryDirectory () as tmpdir :
559+ rtf_path = Path (tmpdir ) / f"{ target_path .stem } .rtf"
560+ rtf_code = self .rtf_encode ()
561+ rtf_path .write_text (rtf_code , encoding = "utf-8" )
562+
563+ with tempfile .TemporaryDirectory () as convert_tmpdir :
564+ converted = converter .convert (
565+ input_files = rtf_path ,
566+ output_dir = Path (convert_tmpdir ),
567+ format = "html" ,
568+ overwrite = True ,
569+ )
570+ if not isinstance (converted , Path ):
571+ raise TypeError (
572+ "LibreOffice conversion returned an unexpected output for a "
573+ "single input file; expected `Path`, got object of type "
574+ f"{ type (converted )!r} with value { converted !r} ."
575+ )
576+ html_path = converted
577+ resources_dir = html_path .with_name (f"{ html_path .name } _files" )
578+ shutil .move (str (html_path ), target_path )
579+ if resources_dir .is_dir ():
580+ shutil .move (
581+ str (resources_dir ), target_path .parent / resources_dir .name
582+ )
583+
584+ print (target_path )
585+
586+ def write_pdf (
587+ self ,
588+ file_path : str | Path ,
589+ * ,
590+ converter : LibreOfficeConverter | None = None ,
591+ ) -> None :
592+ """Write the document as a PDF file.
593+
594+ Writes the document to a temporary RTF file first, and then converts
595+ it to PDF with LibreOffice. Temporary directories are used for
596+ all intermediate files to avoid placing artifacts alongside the
597+ requested output path.
598+
599+ Args:
600+ file_path: Destination path for the PDF file.
601+ Accepts string or Path input. Can be absolute or relative.
602+ Directories are created if they do not already exist.
603+ converter: Optional LibreOffice converter instance.
604+ Pass a configured instance (for example with a custom
605+ `executable_path`) to control how LibreOffice is invoked and to
606+ avoid re-initializing and re-verifying the executable path across
607+ multiple conversions. Note that each call to ``convert()`` still
608+ starts a new LibreOffice process in headless mode; the process is
609+ not kept alive between conversions.
610+
611+ Examples:
612+ ```python
613+ doc = RTFDocument(df=data, rtf_title=RTFTitle(text="Report"))
614+ doc.write_pdf("output/report.pdf")
615+ ```
616+ """
617+ target_path = Path (file_path ).expanduser ()
618+ target_path .parent .mkdir (parents = True , exist_ok = True )
619+
620+ if converter is None :
621+ converter = LibreOfficeConverter ()
622+ with tempfile .TemporaryDirectory () as tmpdir :
623+ rtf_path = Path (tmpdir ) / f"{ target_path .stem } .rtf"
624+ rtf_code = self .rtf_encode ()
625+ rtf_path .write_text (rtf_code , encoding = "utf-8" )
626+
627+ with tempfile .TemporaryDirectory () as convert_tmpdir :
628+ converted = converter .convert (
629+ input_files = rtf_path ,
630+ output_dir = Path (convert_tmpdir ),
631+ format = "pdf" ,
632+ overwrite = True ,
633+ )
634+ if not isinstance (converted , Path ):
635+ raise TypeError (
636+ "LibreOffice conversion returned an unexpected output for a "
637+ "single input file; expected `Path`, got object of type "
638+ f"{ type (converted )!r} with value { converted !r} ."
639+ )
640+ pdf_path = converted
641+ shutil .move (str (pdf_path ), target_path )
642+
643+ print (target_path )
0 commit comments