55import subprocess
66import tempfile
77import textwrap
8+ import warnings
89import webbrowser
910from base64 import b64encode
1011from collections import deque
@@ -298,9 +299,9 @@ class RevealJS(Converter):
298299 Please check out https://revealjs.com/config/ for more details.
299300 """
300301
301- # Export option: use data-uri
302- data_uri : bool = Field (
303- False , description = "Store all animations inside the HTML as data URI ."
302+ # Export option:
303+ one_file : bool = Field (
304+ False , description = "Embed all assets (e.g., animations) inside the HTML."
304305 )
305306 offline : bool = Field (
306307 False , description = "Download remote assets for offline presentation."
@@ -562,11 +563,10 @@ def convert_to(self, dest: Path) -> None: # noqa: C901
562563 )
563564 full_assets_dir = dirname / assets_dir
564565
565- if not self .data_uri or self .offline :
566+ if not self .one_file or self .offline :
566567 logger .debug (f"Assets will be saved to: { full_assets_dir } " )
567- full_assets_dir .mkdir (parents = True , exist_ok = True )
568568
569- if not self .data_uri :
569+ if not self .one_file :
570570 num_presentation_configs = len (self .presentation_configs )
571571
572572 if num_presentation_configs > 1 :
@@ -585,6 +585,7 @@ def prefix(i: int) -> str:
585585 def prefix (i : int ) -> str :
586586 return ""
587587
588+ full_assets_dir .mkdir (parents = True , exist_ok = True )
588589 for i , presentation_config in enumerate (self .presentation_configs ):
589590 presentation_config .copy_to (
590591 full_assets_dir , include_reversed = False , prefix = prefix (i )
@@ -611,28 +612,50 @@ def prefix(i: int) -> str:
611612 get_duration_ms = get_duration_ms ,
612613 has_notes = has_notes ,
613614 env = os .environ ,
614- prefix = prefix if not self .data_uri else None ,
615+ prefix = prefix if not self .one_file else None ,
615616 ** options ,
616617 )
617-
618- if self .offline :
619- soup = BeautifulSoup (content , "html.parser" )
620- session = requests .Session ()
621-
622- for tag , inner in [("link" , "href" ), ("script" , "src" )]:
623- for item in soup .find_all (tag ):
624- if item .has_attr (inner ) and (link := item [inner ]).startswith (
625- "http"
626- ):
627- asset_name = link .rsplit ("/" , 1 )[1 ]
628- asset = session .get (link )
618+ # If not offline, write the content to the file
619+ if not self .offline :
620+ f .write (content )
621+ return
622+
623+ # If offline, download remote assets and store them in the assets folder
624+ soup = BeautifulSoup (content , "html.parser" )
625+ session = requests .Session ()
626+
627+ for tag , inner in [("link" , "href" ), ("script" , "src" )]:
628+ for item in soup .find_all (tag ):
629+ if item .has_attr (inner ) and (link := item [inner ]).startswith (
630+ "http"
631+ ):
632+ asset_name = link .rsplit ("/" , 1 )[1 ]
633+ asset = session .get (link )
634+ if self .one_file :
635+ # If it is a CSS file, inline it
636+ if tag == "link" and "stylesheet" in item ["rel" ]:
637+ item .decompose ()
638+ style = soup .new_tag ("style" )
639+ style .string = asset .text
640+ soup .head .append (style )
641+ # If it is a JS file, inline it
642+ elif tag == "script" :
643+ item .decompose ()
644+ script = soup .new_tag ("script" )
645+ script .string = asset .text
646+ soup .head .append (script )
647+ else :
648+ raise ValueError (
649+ f"Unable to inline { tag } asset: { link } "
650+ )
651+ else :
652+ full_assets_dir .mkdir (parents = True , exist_ok = True )
629653 with open (full_assets_dir / asset_name , "wb" ) as asset_file :
630654 asset_file .write (asset .content )
631655
632656 item [inner ] = str (assets_dir / asset_name )
633657
634- content = str (soup )
635-
658+ content = str (soup )
636659 f .write (content )
637660
638661
@@ -919,6 +942,12 @@ def callback(ctx: Context, param: Parameter, value: bool) -> None:
919942 help = "Use the template given by FILE instead of default one. "
920943 "To echo the default template, use '--show-template'." ,
921944)
945+ @click .option (
946+ "--one-file" ,
947+ is_flag = True ,
948+ help = "Embed all local assets (e.g., video files) in the output file. "
949+ "The is a convenient alias to '-cone_file=true'." ,
950+ )
922951@click .option (
923952 "--offline" ,
924953 is_flag = True ,
@@ -937,6 +966,7 @@ def convert(
937966 config_options : dict [str , str ],
938967 template : Optional [Path ],
939968 offline : bool ,
969+ one_file : bool ,
940970) -> None :
941971 """Convert SCENE(s) into a given format and writes the result in DEST."""
942972 presentation_configs = get_scenes_presentation_config (scenes , folder )
@@ -954,6 +984,28 @@ def convert(
954984 else :
955985 cls = Converter .from_string (to )
956986
987+ if (
988+ one_file
989+ and issubclass (cls , (RevealJS , HtmlZip ))
990+ and "one_file" not in config_options
991+ ):
992+ config_options ["one_file" ] = "true"
993+
994+ # Change data_uri to one_file and print a warning if present
995+ if "data_uri" in config_options :
996+ warnings .warn (
997+ "The 'data_uri' configuration option is deprecated and will be "
998+ "removed in the next major version. "
999+ "Use 'one_file' instead." ,
1000+ DeprecationWarning ,
1001+ stacklevel = 2 ,
1002+ )
1003+ config_options ["one_file" ] = (
1004+ config_options ["one_file" ]
1005+ if "one_file" in config_options
1006+ else config_options .pop ("data_uri" )
1007+ )
1008+
9571009 if (
9581010 offline
9591011 and issubclass (cls , (RevealJS , HtmlZip ))
0 commit comments