@@ -750,6 +750,111 @@ def _ensure_clearpage_before_bibliography(self):
750750 except Exception as e :
751751 self .log (f"Failed to inject \\ clearpage: { e } " , "WARN" )
752752
753+ def _ensure_float_barrier (self ):
754+ """Ensure \\ FloatBarrier before the last \\ section in main.tex.
755+
756+ Prevents figures from floating past the last section (e.g., Conclusion)
757+ into blank pages. Also ensures \\ usepackage{placeins} is in preamble.
758+ """
759+ main_tex = self .latex_dir / "main.tex"
760+ if not main_tex .exists ():
761+ return
762+ try :
763+ import re as _re
764+ content = main_tex .read_text ()
765+
766+ # Ensure \usepackage{placeins}
767+ if 'placeins' not in content :
768+ # Insert after last \usepackage in preamble
769+ pkgs = list (_re .finditer (r'\\usepackage(\[.*?\])?\{[^}]+\}' , content ))
770+ if pkgs :
771+ pos = pkgs [- 1 ].end ()
772+ content = content [:pos ] + '\n \\ usepackage{placeins}' + content [pos :]
773+
774+ # Find all \section positions (not \subsection)
775+ sections = list (_re .finditer (r'(?<!sub)\\section\{' , content ))
776+ if len (sections ) < 2 :
777+ main_tex .write_text (content )
778+ return
779+
780+ # Last section position
781+ last_sec = sections [- 1 ]
782+
783+ # Check if \FloatBarrier already before it
784+ before = content [max (0 , last_sec .start () - 30 ):last_sec .start ()]
785+ if '\\ FloatBarrier' in before :
786+ main_tex .write_text (content )
787+ return
788+
789+ # Insert \FloatBarrier before last \section
790+ insert_pos = last_sec .start ()
791+ content = content [:insert_pos ] + '\\ FloatBarrier\n ' + content [insert_pos :]
792+ main_tex .write_text (content )
793+ self .log (f"Injected \\ FloatBarrier before last section" , "INFO" )
794+ except Exception as e :
795+ self .log (f"Failed to inject FloatBarrier: { e } " , "WARN" )
796+
797+ def _fix_overfull (self , context : str = "pre-delivery" ):
798+ """Fix overfull \\ hbox warnings by asking writer to reword.
799+
800+ Loops until 0 overfull warnings or max attempts.
801+ """
802+ MAX_ATTEMPTS = 3
803+ latex_dir = self .config .get ("latex_dir" , "paper" )
804+
805+ for attempt in range (MAX_ATTEMPTS ):
806+ # Parse overfull from log
807+ log_path = self .latex_dir / "main.log"
808+ if not log_path .exists ():
809+ return
810+
811+ log_text = log_path .read_text (errors = "replace" )
812+ import re as _re
813+ overfull_lines = _re .findall (
814+ r'Overfull \\hbox \(([0-9.]+)pt too wide\) in paragraph at lines (\d+)--(\d+)' ,
815+ log_text
816+ )
817+ # Only fix significant ones (>3pt)
818+ significant = [(float (pt ), int (l1 ), int (l2 )) for pt , l1 , l2 in overfull_lines if float (pt ) > 3.0 ]
819+
820+ if not significant :
821+ if attempt > 0 :
822+ self .log (f"[{ context } ] All overfull warnings fixed (attempt { attempt } )" , "INFO" )
823+ return
824+
825+ if self ._quota_exhausted :
826+ self .log (f"[{ context } ] { len (significant )} overfull warnings remain (quota exhausted)" , "WARN" )
827+ return
828+
829+ self .log (f"[{ context } ] Fixing { len (significant )} overfull warnings (attempt { attempt + 1 } /{ MAX_ATTEMPTS } )..." , "WARN" )
830+
831+ overfull_desc = "\n " .join (
832+ f"- Lines { l1 } -{ l2 } : { pt :.1f} pt too wide" for pt , l1 , l2 in significant [:10 ]
833+ )
834+
835+ self .run_agent ("writer" , f"""## FIX OVERFULL HBOX (attempt { attempt + 1 } )
836+
837+ The paper has { len (significant )} overfull \\ hbox warnings (text extending past column margin):
838+
839+ { overfull_desc }
840+
841+ Fix each by rewording the sentence to allow LaTeX better line breaking:
842+ - Shorten long compound words or phrases
843+ - Add \\ - hyphenation hints for technical terms
844+ - Split long inline math or URLs
845+ - Rephrase to use shorter synonyms
846+
847+ Do NOT change figures, template, or page structure.
848+ After fixing, compile: cd { latex_dir } && pdflatex -interaction=nonstopmode main.tex
849+ """ , timeout = 600 )
850+
851+ self .compile_latex ()
852+
853+ # Final check
854+ remaining = len ([1 for pt , _ , _ in significant if float (pt ) > 3.0 ]) if significant else 0
855+ if remaining :
856+ self .log (f"[{ context } ] { remaining } overfull warnings remain after { MAX_ATTEMPTS } attempts" , "WARN" )
857+
753858 def _run_writing_phase (self , action_plan : dict , prior_context : str = "" ):
754859 """Execute writing phase for all writing tasks."""
755860 issues = action_plan .get ("issues" , [])
0 commit comments