@@ -943,7 +943,7 @@ def _update_field_annotation(
943943 if font_size >= 0
944944 else float (font_properties [font_properties .index ("Tf" ) - 1 ])
945945 )
946- formatting = {
946+ formatting : dict [ str , Union [ bool , int ]] = {
947947 "wrap" : False ,
948948 "scale" : False ,
949949 }
@@ -959,6 +959,7 @@ def _update_field_annotation(
959959 font_height = rct .height - 2
960960 formatting ["scale" ] = True
961961 font_properties [font_properties .index ("Tf" ) - 1 ] = str (font_height )
962+ formatting ["align" ] = field .get ("/Q" , 0 )
962963
963964 # Retrieve font information from local DR ...
964965 dr : Any = cast (
@@ -3554,7 +3555,7 @@ def generate_appearance_stream(
35543555 font_descriptor : FontDescriptor ,
35553556 rct : RectangleObject ,
35563557 font_height : float ,
3557- formatting : dict [str , bool ],
3558+ formatting : dict [str , Union [ bool , int ] ],
35583559) -> bytes :
35593560 # Only wrap text for non-choice fields, otherwise we break matching sel and line later on.
35603561 if sel :
@@ -3567,7 +3568,7 @@ def generate_appearance_stream(
35673568 # takes one extra point (see below, under "line_number == 0:")
35683569 rct .height - 3 , # One point margin for top and bottom, one point extra for the first line (see y_offset)
35693570 txt ,
3570- formatting ["wrap" ],
3571+ bool ( formatting ["wrap" ]) ,
35713572 )
35723573 font_properties [1 ] = str (font_height )
35733574 else :
@@ -3576,7 +3577,11 @@ def generate_appearance_stream(
35763577 y_offset = rct .height - 1 - font_height
35773578 da = " " .join (font_properties )
35783579 ap_stream = f"q\n /Tx BMC \n q\n 1 1 { rct .width - 1 } { rct .height - 1 } re\n W\n BT\n { da } \n " .encode ()
3580+
3581+ current_x_pos : float = 0 # Initial virtual position within the text object.
35793582 for line_number , line in enumerate (lines ):
3583+ # Calculate line width
3584+ line_w = calculate_text_width (font_descriptor .character_widths , float (font_properties [1 ]), line )
35803585 # Escape parentheses (PDF 1.7 reference, table 3.2, Literal Strings)
35813586 line = line .replace ("\\ " , "\\ \\ " ).replace ("(" , r"\(" ).replace (")" , r"\)" )
35823587 if line in sel :
@@ -3585,11 +3590,33 @@ def generate_appearance_stream(
35853590 f"1 { y_offset - (line_number * font_height * 1.4 ) - 1 } { rct .width - 2 } { font_height + 2 } re\n "
35863591 f"0.5 0.5 0.5 rg s\n { da } \n "
35873592 ).encode ()
3593+
3594+ # Calculate the desired absolute starting X for the current line
3595+ desired_abs_x_start : float = 0
3596+ if formatting ["align" ] == 2 : # Right aligned
3597+ desired_abs_x_start = rct .width - 2 - line_w
3598+ elif formatting ["align" ] == 1 : # Centered
3599+ desired_abs_x_start = (rct .width - line_w ) / 2
3600+ else : # Left aligned; default
3601+ desired_abs_x_start = 2
3602+ # Calculate x_rel_offset: how much to move from the current_x_pos
3603+ # to reach the desired_abs_x_start.
3604+ x_rel_offset = desired_abs_x_start - current_x_pos
3605+
3606+ # Y-offset:
3607+ y_rel_offset : float = 0
35883608 if line_number == 0 :
3589- ap_stream += f"2 { y_offset } Td \n " . encode ()
3609+ y_rel_offset = y_offset # Initial vertical position
35903610 else :
3591- # Td is a relative translation
3592- ap_stream += f"0 { - font_height * 1.4 } Td\n " .encode ()
3611+ y_rel_offset = - font_height * 1.4 # Move down by line height
3612+
3613+ # Td is a relative translation (Tx and Ty).
3614+ # It updates the current text position.
3615+ ap_stream += f"{ x_rel_offset } { y_rel_offset } Td\n " .encode ()
3616+ # Update current_x_pos based on the Td operation for the next iteration.
3617+ # This is the X position where the *current line* will start.
3618+ current_x_pos = desired_abs_x_start
3619+
35933620 enc_line : list [bytes ] = [
35943621 font_full_rev .get (c , c .encode ("utf-16-be" )) for c in line
35953622 ]
0 commit comments