@@ -12,13 +12,33 @@ use tauri::{
1212 menu:: { AboutMetadata , MenuBuilder , MenuItemBuilder , SubmenuBuilder } ,
1313} ;
1414
15+ use clap:: { Parser , ValueEnum } ;
1516use tauri_plugin_dialog:: DialogExt ;
1617use tinymist_project:: {
1718 CompileFontArgs , CompileOnceArgs , EntryReader , TaskInputs , WorldProvider , base:: ShadowApi ,
1819} ;
1920use typst_pdf:: PdfOptions ;
2021use typstyle_core:: Typstyle ;
2122
23+ mod version;
24+
25+ #[ derive( Debug , Clone , ValueEnum ) ]
26+ enum OutputFormat {
27+ Typst ,
28+ PDF ,
29+ }
30+
31+ /// Simple program to greet a person
32+ #[ derive( Parser , Debug ) ]
33+ #[ command( version = version:: VERSION , about, long_about = None , display_name="TyX" ) ]
34+ struct Args {
35+ /// Files to open or export.
36+ files : Vec < String > ,
37+ /// Export as this file format.
38+ #[ arg( short, long) ]
39+ export : Option < OutputFormat > ,
40+ }
41+
2242#[ tauri:: command]
2343fn save ( filename : & str , content : & str , format : bool ) {
2444 let mut content = String :: from ( content) ;
@@ -43,13 +63,14 @@ fn saveas(handle: tauri::AppHandle) {
4363 . add_filter ( "TyX" , & [ "tyx" ] )
4464 . save_file ( move |f| {
4565 if let Some ( f) = f
46- && let Some ( path) = f. as_path ( ) {
47- let mut path = path. to_str ( ) . unwrap ( ) . to_string ( ) ;
48- if !path. ends_with ( ".tyx" ) {
49- path += ".tyx" ;
50- }
51- h. emit ( "saveas" , ( path, ) ) . unwrap ( ) ;
66+ && let Some ( path) = f. as_path ( )
67+ {
68+ let mut path = path. to_str ( ) . unwrap ( ) . to_string ( ) ;
69+ if !path. ends_with ( ".tyx" ) {
70+ path += ".tyx" ;
5271 }
72+ h. emit ( "saveas" , ( path, ) ) . unwrap ( ) ;
73+ }
5374 } ) ;
5475}
5576
@@ -110,9 +131,10 @@ fn open(handle: tauri::AppHandle, filename: &str) {
110131 . add_filter ( "Typst" , & [ "typ" ] )
111132 . pick_file ( move |f| {
112133 if let Some ( f) = f
113- && let Some ( path) = f. as_path ( ) {
114- openfile ( & h, path, true ) ;
115- }
134+ && let Some ( path) = f. as_path ( )
135+ {
136+ openfile ( & h, path, true ) ;
137+ }
116138 } ) ;
117139}
118140
@@ -131,9 +153,10 @@ fn newfromtemplate(handle: tauri::AppHandle) {
131153 . set_directory ( settings_path)
132154 . pick_file ( move |f| {
133155 if let Some ( f) = f
134- && let Some ( path) = f. as_path ( ) {
135- openfile ( & h, path, false ) ;
136- }
156+ && let Some ( path) = f. as_path ( )
157+ {
158+ openfile ( & h, path, false ) ;
159+ }
137160 } ) ;
138161}
139162
@@ -190,6 +213,57 @@ fn readimage(filename: &str, image: &str) -> String {
190213 String :: from ( "data:" ) + mimetype + ";base64," + STANDARD . encode ( & bytes) . as_str ( )
191214}
192215
216+ fn typst_to_pdf (
217+ filename : & str ,
218+ content : & str ,
219+ root_path : PathBuf ,
220+ font_paths : Vec < PathBuf > ,
221+ ) -> Result < Vec < u8 > , String > {
222+ let filename_path = PathBuf :: from ( & filename) ;
223+ if !filename_path. is_file ( ) {
224+ File :: create ( & filename_path) . unwrap ( ) ;
225+ }
226+ let filename_path = dunce:: canonicalize ( filename_path) . unwrap ( ) ;
227+ let universe = CompileOnceArgs {
228+ root : Some ( dunce:: canonicalize ( & root_path) . unwrap ( ) ) ,
229+ input : Some ( filename_path. to_str ( ) . unwrap ( ) . to_string ( ) ) ,
230+ font : CompileFontArgs {
231+ font_paths,
232+ ..CompileFontArgs :: default ( )
233+ } ,
234+ ..CompileOnceArgs :: default ( )
235+ }
236+ . resolve ( )
237+ . unwrap ( ) ;
238+ let entry = match universe
239+ . entry_state ( )
240+ . try_select_path_in_workspace ( & filename_path)
241+ {
242+ Ok ( entry) => entry,
243+ Err ( _e) => {
244+ return Err ( "Invalid root directory, couldn't select the file itself!" . into ( ) ) ;
245+ }
246+ } ;
247+ let mut world = universe. snapshot_with ( Some ( TaskInputs {
248+ entry,
249+ ..TaskInputs :: default ( )
250+ } ) ) ;
251+ let main = world. main_id ( ) . unwrap ( ) ;
252+ let _ = world. map_shadow_by_id (
253+ main,
254+ typst:: foundations:: Bytes :: from_string ( content. to_owned ( ) ) ,
255+ ) ;
256+ let doc = match typst:: compile ( & world) . output {
257+ Ok ( doc) => doc,
258+ Err ( e) => {
259+ return Err ( e[ 0 ] . message . to_string ( ) ) ;
260+ }
261+ } ;
262+ let pdf = typst_pdf:: pdf ( & doc, & PdfOptions :: default ( ) ) . unwrap ( ) ;
263+
264+ Ok ( pdf)
265+ }
266+
193267#[ tauri:: command]
194268fn preview (
195269 handle : tauri:: AppHandle ,
@@ -237,51 +311,16 @@ fn preview(
237311 dunce:: canonicalize ( & p) . unwrap ( )
238312 } )
239313 . collect :: < Vec < PathBuf > > ( ) ;
314+
240315 font_paths. insert ( 0 , tyx_fonts_path) ;
241- let filename_path = PathBuf :: from ( & filename) ;
242- if !filename_path. is_file ( ) {
243- File :: create ( & filename_path) . unwrap ( ) ;
244- }
245- let filename_path = dunce:: canonicalize ( filename_path) . unwrap ( ) ;
246- let universe = CompileOnceArgs {
247- root : Some ( dunce:: canonicalize ( & root_path) . unwrap ( ) ) ,
248- input : Some ( filename_path. to_str ( ) . unwrap ( ) . to_string ( ) ) ,
249- font : CompileFontArgs {
250- font_paths,
251- ..CompileFontArgs :: default ( )
252- } ,
253- ..CompileOnceArgs :: default ( )
254- }
255- . resolve ( )
256- . unwrap ( ) ;
257- let entry = match universe
258- . entry_state ( )
259- . try_select_path_in_workspace ( & filename_path)
260- {
261- Ok ( entry) => entry,
262- Err ( _e) => {
263- return String :: from ( "Invalid root directory, couldn't select the file itself!" ) ;
264- }
265- } ;
266- let mut world = universe. snapshot_with ( Some ( TaskInputs {
267- entry,
268- ..TaskInputs :: default ( )
269- } ) ) ;
270- let main = world. main_id ( ) . unwrap ( ) ;
271- let _ = world. map_shadow_by_id (
272- main,
273- typst:: foundations:: Bytes :: from_string ( content. to_owned ( ) ) ,
274- ) ;
275- let doc = match typst:: compile ( & world) . output {
276- Ok ( doc) => doc,
316+
317+ let pdf = match typst_to_pdf ( & filename, content, root_path, font_paths) {
318+ Ok ( pdf) => pdf,
277319 Err ( e) => {
278- if e. is_empty ( ) {
279- return String :: new ( ) ;
280- }
281- return e[ 0 ] . message . to_string ( ) ;
320+ return e;
282321 }
283322 } ;
284- let pdf = typst_pdf :: pdf ( & doc , & PdfOptions :: default ( ) ) . unwrap ( ) ;
323+
285324 let mut f = File :: create ( & pdf_file) . unwrap ( ) ;
286325 f. write_all ( & pdf) . unwrap ( ) ;
287326
@@ -308,13 +347,14 @@ fn insertimage(handle: tauri::AppHandle, filename: &str) {
308347 . add_filter ( "Image" , & [ "png" , "jpg" , "jpeg" , "gif" , "svg" ] )
309348 . pick_file ( move |f| {
310349 if let Some ( f) = f
311- && let Some ( path) = f. as_path ( ) {
312- h. emit (
313- "insertImage" ,
314- ( path. strip_prefix ( dirname) . unwrap_or ( path) . to_str ( ) . unwrap ( ) , ) ,
315- )
316- . unwrap ( ) ;
317- }
350+ && let Some ( path) = f. as_path ( )
351+ {
352+ h. emit (
353+ "insertImage" ,
354+ ( path. strip_prefix ( dirname) . unwrap_or ( path) . to_str ( ) . unwrap ( ) , ) ,
355+ )
356+ . unwrap ( ) ;
357+ }
318358 } ) ;
319359}
320360
@@ -354,6 +394,38 @@ pub fn run() {
354394
355395#[ cfg( not( any( target_os = "android" , target_os = "ios" ) ) ) ]
356396pub fn run ( ) {
397+ let args = Args :: parse ( ) ;
398+
399+ if let Some ( format) = args. export {
400+ for file in args. files {
401+ let file_base = match file. strip_suffix ( ".tyx" ) {
402+ Some ( file) => file,
403+ None => {
404+ println ! ( "warning: file {file} might not be a TyX document!" ) ;
405+ & file
406+ }
407+ } ;
408+ let dirname = Path :: new ( & file) . parent ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) ;
409+ let contents = std:: fs:: read_to_string ( & file) . unwrap ( ) ;
410+ let result = match format {
411+ OutputFormat :: Typst => {
412+ tyx_converters:: serialized_tyx_to_typst ( & contents) . into_bytes ( )
413+ }
414+ OutputFormat :: PDF => {
415+ let contents = tyx_converters:: serialized_tyx_to_typst ( & contents) ;
416+ typst_to_pdf ( & file, & contents, PathBuf :: from ( dirname) , vec ! [ ] ) . unwrap ( )
417+ }
418+ } ;
419+ let file_extension = match format {
420+ OutputFormat :: Typst => ".typ" ,
421+ OutputFormat :: PDF => ".pdf" ,
422+ } ;
423+
424+ std:: fs:: write ( String :: from ( file_base) + file_extension, result) . unwrap ( ) ;
425+ }
426+ return ;
427+ }
428+
357429 tauri:: Builder :: default ( )
358430 . plugin ( tauri_plugin_process:: init ( ) )
359431 . plugin ( tauri_plugin_updater:: Builder :: new ( ) . build ( ) )
0 commit comments