@@ -109,6 +109,21 @@ impl<'a> EngineState<'a> {
109109
110110 Ok ( ( ) )
111111 }
112+
113+ /// Return true if we're in the initializing phase, but not in the midst of
114+ /// a multi-step construct like startDefineFontFamily. In such situations,
115+ /// if we see an event that is associated with the beginning of the actual
116+ /// content, we should end the initialization phase.
117+ fn in_endable_init ( & self ) -> bool {
118+ match & self . state {
119+ State :: Invalid => false ,
120+ State :: Initializing ( s) => {
121+ s. cur_font_family_definition . is_none ( )
122+ && s. cur_font_family_tag_associations . is_none ( )
123+ }
124+ State :: Emitting ( _) => false ,
125+ }
126+ }
112127}
113128
114129impl < ' a > XdvEvents for EngineState < ' a > {
@@ -125,14 +140,46 @@ impl<'a> XdvEvents for EngineState<'a> {
125140 fn handle_special ( & mut self , x : i32 , y : i32 , contents : & [ u8 ] ) -> Result < ( ) > {
126141 let contents = atry ! ( std:: str :: from_utf8( contents) ; [ "could not parse \\ special as UTF-8" ] ) ;
127142
128- if contents == "tdux:emit" || contents. starts_with ( "tdux:provideFile" ) {
129- self . state . ensure_initialized ( ) ?;
143+ // str.split_once() would be nice but it was introduced in 1.52 which is
144+ // a bit recent for us.
145+
146+ let mut pieces = contents. splitn ( 2 , ' ' ) ;
147+
148+ let ( tdux_command, remainder) = if let Some ( p) = pieces. next ( ) {
149+ if let Some ( cmd) = p. strip_prefix ( "tdux:" ) {
150+ ( Some ( cmd) , pieces. next ( ) . unwrap_or_default ( ) )
151+ } else {
152+ ( None , contents)
153+ }
154+ } else {
155+ ( None , contents)
156+ } ;
157+
158+ // Might we need to end the initialization phase?
159+
160+ if self . in_endable_init ( ) {
161+ let end_init = if let Some ( cmd) = tdux_command {
162+ match cmd {
163+ "emit" | "provideFile" | "asp" | "aep" | "cs" | "ce" | "mfs" | "me" | "dt" => {
164+ true
165+ }
166+ _ => false ,
167+ }
168+ } else {
169+ false
170+ } ;
171+
172+ if end_init {
173+ self . state . ensure_initialized ( ) ?;
174+ }
130175 }
131176
177+ // Ready to dispatch.
178+
132179 match & mut self . state {
133180 State :: Invalid => panic ! ( "invalid spx2html state leaked" ) ,
134- State :: Initializing ( s) => s. handle_special ( contents , & mut self . common ) ,
135- State :: Emitting ( s) => s. handle_special ( x, y, contents , & mut self . common ) ,
181+ State :: Initializing ( s) => s. handle_special ( tdux_command , remainder , & mut self . common ) ,
182+ State :: Emitting ( s) => s. handle_special ( x, y, tdux_command , remainder , & mut self . common ) ,
136183 }
137184 }
138185
@@ -145,20 +192,7 @@ impl<'a> XdvEvents for EngineState<'a> {
145192 x : & [ i32 ] ,
146193 y : & [ i32 ] ,
147194 ) -> Result < ( ) > {
148- // Here we have a quasi-hack because the initialization stage needs to
149- // handle text-and-glyphs, *if* it's in a font-family definition.
150- // Otherwise it's time to exit the initialization mode.
151-
152- let do_init = match & mut self . state {
153- State :: Invalid => false ,
154- State :: Initializing ( s) => {
155- s. cur_font_family_definition . is_none ( )
156- && s. cur_font_family_tag_associations . is_none ( )
157- }
158- State :: Emitting ( _) => false ,
159- } ;
160-
161- if do_init {
195+ if self . in_endable_init ( ) {
162196 self . state . ensure_initialized ( ) ?;
163197 }
164198
@@ -378,26 +412,37 @@ impl InitializationState {
378412 Ok ( ( ) )
379413 }
380414
381- fn handle_special ( & mut self , contents : & str , common : & mut Common ) -> Result < ( ) > {
382- if let Some ( texpath) = contents. strip_prefix ( "tdux:addTemplate " ) {
383- self . handle_add_template ( texpath, common)
384- } else if let Some ( texpath) = contents. strip_prefix ( "tdux:setTemplate " ) {
385- self . handle_set_template ( texpath, common)
386- } else if let Some ( texpath) = contents. strip_prefix ( "tdux:setOutputPath " ) {
387- self . handle_set_output_path ( texpath, common)
388- } else if let Some ( remainder) = contents. strip_prefix ( "tdux:setTemplateVariable " ) {
389- self . handle_set_template_variable ( remainder, common)
390- } else if contents == "tdux:startDefineFontFamily" {
391- self . handle_start_define_font_family ( )
392- } else if contents == "tdux:endDefineFontFamily" {
393- self . handle_end_define_font_family ( common)
394- } else if contents == "tdux:startFontFamilyTagAssociations" {
395- self . handle_start_font_family_tag_associations ( )
396- } else if contents == "tdux:endFontFamilyTagAssociations" {
397- self . handle_end_font_family_tag_associations ( common)
398- } else if let Some ( _remainder) = contents. strip_prefix ( "tdux:provideFile " ) {
399- tt_warning ! ( common. status, "ignoring too-soon tdux:provideFile special" ) ;
400- Ok ( ( ) )
415+ fn handle_special (
416+ & mut self ,
417+ tdux_command : Option < & str > ,
418+ remainder : & str ,
419+ common : & mut Common ,
420+ ) -> Result < ( ) > {
421+ if let Some ( cmd) = tdux_command {
422+ match cmd {
423+ "addTemplate" => self . handle_add_template ( remainder, common) ,
424+ "setTemplate" => self . handle_set_template ( remainder, common) ,
425+ "setOutputPath" => self . handle_set_output_path ( remainder, common) ,
426+ "setTemplateVariable" => self . handle_set_template_variable ( remainder, common) ,
427+
428+ "startDefineFontFamily" => self . handle_start_define_font_family ( ) ,
429+ "endDefineFontFamily" => self . handle_end_define_font_family ( common) ,
430+
431+ "startFontFamilyTagAssociations" => {
432+ self . handle_start_font_family_tag_associations ( )
433+ }
434+
435+ "endFontFamilyTagAssociations" => {
436+ self . handle_end_font_family_tag_associations ( common)
437+ }
438+
439+ "provideFile" => {
440+ tt_warning ! ( common. status, "ignoring too-soon tdux:provideFile special" ) ;
441+ Ok ( ( ) )
442+ }
443+
444+ _ => Ok ( ( ) ) ,
445+ }
401446 } else {
402447 Ok ( ( ) )
403448 }
@@ -976,105 +1021,132 @@ impl EmittingState {
9761021 & mut self ,
9771022 x : i32 ,
9781023 y : i32 ,
979- contents : & str ,
1024+ tdux_command : Option < & str > ,
1025+ remainder : & str ,
9801026 common : & mut Common ,
9811027 ) -> Result < ( ) > {
982- if contents == "tdux:asp" {
983- if self . content_finished {
984- self . warn_finished_content ( "auto start paragraph" , common) ;
985- } else if self . cur_elstate ( ) . do_auto_tags {
986- // Why are we using <div>s instead of <p>? As the HTML spec
987- // emphasizes, <p> tags are structural, not semantic. You cannot
988- // put tags like <ul> or <div> inside <p> -- they automatically
989- // close the paragraph. This does not align with TeX's idea of a
990- // paragraph, and there's no upside to trying to use <p>'s -- as
991- // the spec notes, the <p> tag does not activate any important
992- // semantics itself. The HTML spec explicitly recommends that
993- // you can use <div> elements to group logical paragraphs. So
994- // that's what we do.
995- let el = self . create_elem ( "div" , true , common) ;
996- self . push_space_if_needed ( x, None ) ;
997- self . current_content . push_str ( "<div class=\" tdux-p\" >" ) ;
998- self . push_elem ( el, ElementOrigin :: EngineAuto ) ;
999- }
1000- Ok ( ( ) )
1001- } else if contents == "tdux:aep" {
1002- if self . content_finished {
1003- self . warn_finished_content ( "auto end paragraph" , common) ;
1004- } else if self . cur_elstate ( ) . do_auto_tags {
1005- self . pop_elem ( "div" , common) ;
1006- }
1007- Ok ( ( ) )
1008- } else if let Some ( kind) = contents. strip_prefix ( "tdux:cs " ) {
1009- if self . content_finished {
1010- self . warn_finished_content ( "canvas start" , common) ;
1011- } else if let Some ( canvas) = self . current_canvas . as_mut ( ) {
1012- canvas. depth += 1 ;
1013- } else {
1014- self . current_canvas = Some ( CanvasState :: new ( kind, x, y) ) ;
1015- }
1016- Ok ( ( ) )
1017- } else if let Some ( _kind) = contents. strip_prefix ( "tdux:ce " ) {
1018- if self . content_finished {
1019- self . warn_finished_content ( "canvas end" , common) ;
1020- } else if let Some ( canvas) = self . current_canvas . as_mut ( ) {
1021- canvas. depth -= 1 ;
1022- if canvas. depth == 0 {
1023- self . handle_end_canvas ( common) ?;
1028+ if let Some ( cmd) = tdux_command {
1029+ match cmd {
1030+ "asp" => {
1031+ if self . content_finished {
1032+ self . warn_finished_content ( "auto start paragraph" , common) ;
1033+ } else if self . cur_elstate ( ) . do_auto_tags {
1034+ // Why are we using <div>s instead of <p>? As the HTML spec
1035+ // emphasizes, <p> tags are structural, not semantic. You cannot
1036+ // put tags like <ul> or <div> inside <p> -- they automatically
1037+ // close the paragraph. This does not align with TeX's idea of a
1038+ // paragraph, and there's no upside to trying to use <p>'s -- as
1039+ // the spec notes, the <p> tag does not activate any important
1040+ // semantics itself. The HTML spec explicitly recommends that
1041+ // you can use <div> elements to group logical paragraphs. So
1042+ // that's what we do.
1043+ let el = self . create_elem ( "div" , true , common) ;
1044+ self . push_space_if_needed ( x, None ) ;
1045+ self . current_content . push_str ( "<div class=\" tdux-p\" >" ) ;
1046+ self . push_elem ( el, ElementOrigin :: EngineAuto ) ;
1047+ }
1048+ Ok ( ( ) )
1049+ }
1050+
1051+ "aep" => {
1052+ if self . content_finished {
1053+ self . warn_finished_content ( "auto end paragraph" , common) ;
1054+ } else if self . cur_elstate ( ) . do_auto_tags {
1055+ self . pop_elem ( "div" , common) ;
1056+ }
1057+ Ok ( ( ) )
1058+ }
1059+
1060+ "cs" => {
1061+ if self . content_finished {
1062+ self . warn_finished_content ( "canvas start" , common) ;
1063+ } else if let Some ( canvas) = self . current_canvas . as_mut ( ) {
1064+ canvas. depth += 1 ;
1065+ } else {
1066+ self . current_canvas = Some ( CanvasState :: new ( remainder, x, y) ) ;
1067+ }
1068+ Ok ( ( ) )
1069+ }
1070+
1071+ "ce" => {
1072+ if self . content_finished {
1073+ self . warn_finished_content ( "canvas end" , common) ;
1074+ } else if let Some ( canvas) = self . current_canvas . as_mut ( ) {
1075+ canvas. depth -= 1 ;
1076+ if canvas. depth == 0 {
1077+ self . handle_end_canvas ( common) ?;
1078+ }
1079+ } else {
1080+ tt_warning ! (
1081+ common. status,
1082+ "ignoring unpaired tdux:c[anvas]e[nd] special for `{}`" ,
1083+ remainder
1084+ ) ;
1085+ }
1086+ Ok ( ( ) )
1087+ }
1088+
1089+ "mfs" => {
1090+ if self . content_finished {
1091+ self . warn_finished_content (
1092+ & format ! ( "manual flexible start tag {:?}" , remainder) ,
1093+ common,
1094+ ) ;
1095+ Ok ( ( ) )
1096+ } else {
1097+ self . handle_flexible_start_tag ( x, y, remainder, common)
1098+ }
1099+ }
1100+
1101+ "me" => {
1102+ if self . content_finished {
1103+ self . warn_finished_content (
1104+ & format ! ( "manual end tag </{}>" , remainder) ,
1105+ common,
1106+ ) ;
1107+ } else {
1108+ self . pop_elem ( remainder, common) ;
1109+ }
1110+ Ok ( ( ) )
1111+ }
1112+
1113+ "dt" => {
1114+ if self . content_finished {
1115+ self . warn_finished_content ( "direct text" , common) ;
1116+ } else {
1117+ html_escape:: encode_safe_to_string ( remainder, & mut self . current_content ) ;
1118+ }
1119+ Ok ( ( ) )
1120+ }
1121+
1122+ "emit" => self . finish_file ( common) ,
1123+
1124+ "setTemplate" => {
1125+ self . next_template_path = remainder. to_owned ( ) ;
1126+ Ok ( ( ) )
1127+ }
1128+
1129+ "setOutputPath" => {
1130+ self . next_output_path = remainder. to_owned ( ) ;
1131+ Ok ( ( ) )
1132+ }
1133+
1134+ "setTemplateVariable" => self . handle_set_template_variable ( remainder, common) ,
1135+
1136+ "provideFile" => self . handle_provide_file ( remainder, common) ,
1137+
1138+ "contentFinished" => self . content_finished ( common) ,
1139+
1140+ other => {
1141+ tt_warning ! (
1142+ common. status,
1143+ "ignoring unrecognized special: tdux:{} {}" ,
1144+ other,
1145+ remainder
1146+ ) ;
1147+ Ok ( ( ) )
10241148 }
1025- } else {
1026- tt_warning ! (
1027- common. status,
1028- "ignoring unpaired tdux:c[anvas]e[nd] special `{}`" ,
1029- contents
1030- ) ;
1031- }
1032- Ok ( ( ) )
1033- } else if let Some ( remainder) = contents. strip_prefix ( "tdux:mfs " ) {
1034- if self . content_finished {
1035- self . warn_finished_content (
1036- & format ! ( "manual flexible start tag {:?}" , remainder) ,
1037- common,
1038- ) ;
1039- Ok ( ( ) )
1040- } else {
1041- self . handle_flexible_start_tag ( x, y, remainder, common)
1042- }
1043- } else if let Some ( element) = contents. strip_prefix ( "tdux:me " ) {
1044- if self . content_finished {
1045- self . warn_finished_content ( & format ! ( "manual end tag </{}>" , element) , common) ;
1046- } else {
1047- self . pop_elem ( element, common) ;
1048- }
1049- Ok ( ( ) )
1050- } else if let Some ( direct_text) = contents. strip_prefix ( "tdux:dt " ) {
1051- if self . content_finished {
1052- self . warn_finished_content ( "direct text" , common) ;
1053- } else {
1054- html_escape:: encode_safe_to_string ( direct_text, & mut self . current_content ) ;
10551149 }
1056- Ok ( ( ) )
1057- } else if contents == "tdux:emit" {
1058- self . finish_file ( common)
1059- } else if let Some ( texpath) = contents. strip_prefix ( "tdux:setTemplate " ) {
1060- self . next_template_path = texpath. to_owned ( ) ;
1061- Ok ( ( ) )
1062- } else if let Some ( texpath) = contents. strip_prefix ( "tdux:setOutputPath " ) {
1063- self . next_output_path = texpath. to_owned ( ) ;
1064- Ok ( ( ) )
1065- } else if let Some ( remainder) = contents. strip_prefix ( "tdux:setTemplateVariable " ) {
1066- self . handle_set_template_variable ( remainder, common)
1067- } else if let Some ( remainder) = contents. strip_prefix ( "tdux:provideFile " ) {
1068- self . handle_provide_file ( remainder, common)
1069- } else if contents == "tdux:contentFinished" {
1070- self . content_finished ( common)
1071- } else if let Some ( remainder) = contents. strip_prefix ( "tdux:" ) {
1072- tt_warning ! (
1073- common. status,
1074- "ignoring unrecognized special: tdux:{}" ,
1075- remainder
1076- ) ;
1077- Ok ( ( ) )
10781150 } else {
10791151 Ok ( ( ) )
10801152 }
0 commit comments