@@ -33,31 +33,28 @@ public function layout(array $nodes, float $width, float $height): LayoutResult
3333
3434 foreach ($ this ->walk ($ nodes ) as $ node ) {
3535 $ style = $ this ->styleParser ->parse ($ node ->attributes ['style ' ] ?? '' );
36- $ margin = $ this ->parseBoxSpacing (( string ) $ style -> get ( 'margin ' , '0 ' ));
37- $ padding = $ this ->parseBoxSpacing (( string ) $ style -> get ( 'padding ' , '0 ' ));
36+ $ margin = $ this ->parseBoxSpacing ($ this -> styleValue ( $ style , 'margin ' , '0 ' ));
37+ $ padding = $ this ->parseBoxSpacing ($ this -> styleValue ( $ style , 'padding ' , '0 ' ));
3838
3939 $ cursorY -= ($ margin ['top ' ] + $ padding ['top ' ]);
4040
41- $ fontSize = $ this ->toPoints ((string ) $ style ->get ('font-size ' , '10 ' ));
42- $ lineHeight = max (
43- $ fontSize * 1.2 ,
44- $ this ->toPoints ((string ) $ style ->get ('line-height ' , (string ) ($ fontSize * 1.2 ))),
45- );
41+ $ fontSize = $ this ->toPoints ($ this ->styleValue ($ style , 'font-size ' , '10 ' ));
42+ $ lineHeight = $ this ->resolveLineHeight ($ style , $ fontSize );
4643 $ fontAlias = $ this ->resolveFontAlias (
47- ( string ) $ style -> get ( 'font-family ' , 'helvetica ' ),
48- ( string ) $ style -> get ( 'font-weight ' , 'normal ' ),
44+ $ this -> styleValue ( $ style , 'font-family ' , 'helvetica ' ),
45+ $ this -> styleValue ( $ style , 'font-weight ' , 'normal ' ),
4946 );
5047
51- $ boxWidth = $ this ->toPoints (( string ) $ style -> get ( 'width ' , '0 ' ));
48+ $ boxWidth = $ this ->toPoints ($ this -> styleValue ( $ style , 'width ' , '0 ' ));
5249 if ($ boxWidth <= 0 ) {
5350 $ boxWidth = max ($ width - $ margin ['left ' ] - $ margin ['right ' ] - $ padding ['left ' ] - $ padding ['right ' ], 0 );
5451 }
5552 $ leftBase = $ margin ['left ' ] + $ padding ['left ' ];
5653 $ rightBase = $ leftBase + $ boxWidth ;
5754
5855 if ($ node ->tag === 'img ' ) {
59- $ imgWidth = $ this ->toPoints (( string ) $ style -> get ( 'width ' , '32 ' ));
60- $ imgHeight = $ this ->toPoints (( string ) $ style -> get ( 'height ' , '32 ' ));
56+ $ imgWidth = $ this ->toPoints ($ this -> styleValue ( $ style , 'width ' , '32 ' ));
57+ $ imgHeight = $ this ->toPoints ($ this -> styleValue ( $ style , 'height ' , '32 ' ));
6158 if ($ imgWidth <= 0 ) {
6259 $ imgWidth = 32.0 ;
6360 }
@@ -88,20 +85,20 @@ public function layout(array $nodes, float $width, float $height): LayoutResult
8885 continue ;
8986 }
9087
91- $ align = strtolower (( string ) $ style -> get ( 'text-align ' , 'left ' ));
92- $ x = match ($ align ) {
88+ $ align = strtolower ($ this -> styleValue ( $ style , 'text-align ' , 'left ' ));
89+ $ lineX = match ($ align ) {
9390 'center ' => $ leftBase + ($ boxWidth / 2.0 ),
9491 'right ' => max ($ rightBase - 8.0 , 0 ),
9592 default => $ leftBase + 8.0 ,
9693 };
9794
9895 $ lines [] = new LayoutLine (
9996 text: $ text ,
100- x: $ x ,
97+ x: $ lineX ,
10198 y: max ($ cursorY , 0 ),
10299 fontSize: $ fontSize ,
103100 fontAlias: $ fontAlias ,
104- rgbColor: ( string ) $ style -> get ( 'color ' , '#000000 ' ),
101+ rgbColor: $ this -> styleValue ( $ style , 'color ' , '#000000 ' ),
105102 );
106103
107104 $ cursorY -= ($ lineHeight + $ margin ['bottom ' ] + $ padding ['bottom ' ]);
@@ -110,17 +107,33 @@ public function layout(array $nodes, float $width, float $height): LayoutResult
110107 return new LayoutResult (lines: $ lines , images: $ images );
111108 }
112109
110+ private function styleValue (
111+ \LibreSign \XObjectTemplate \Css \StyleMap $ style ,
112+ string $ property ,
113+ string $ default ,
114+ ): string {
115+ return $ style ->get ($ property , $ default ) ?? $ default ;
116+ }
117+
113118 /**
114119 * @param list<Node> $nodes
115120 * @return list<Node>
116121 */
117122 private function walk (array $ nodes ): array
118123 {
119124 $ result = [];
120- foreach ($ nodes as $ node ) {
125+ $ stack = array_reverse ($ nodes );
126+
127+ while ($ stack !== []) {
128+ $ node = array_pop ($ stack );
121129 $ result [] = $ node ;
122- if ($ node ->children !== []) {
123- $ result = [...$ result , ...$ this ->walk ($ node ->children )];
130+
131+ if ($ node ->children === []) {
132+ continue ;
133+ }
134+
135+ foreach (array_reverse ($ node ->children ) as $ child ) {
136+ $ stack [] = $ child ;
124137 }
125138 }
126139
@@ -129,11 +142,7 @@ private function walk(array $nodes): array
129142
130143 private function toPoints (string $ value ): float
131144 {
132- $ normalized = trim (strtolower ($ value ));
133- if ($ normalized === '' ) {
134- return 0.0 ;
135- }
136-
145+ $ normalized = strtolower ($ value );
137146 $ number = (float ) preg_replace ('/[^0-9.\-]/ ' , '' , $ normalized );
138147 if (str_ends_with ($ normalized , 'px ' )) {
139148 return $ number * 0.75 ;
@@ -142,13 +151,27 @@ private function toPoints(string $value): float
142151 return $ number ;
143152 }
144153
154+ private function resolveLineHeight (
155+ \LibreSign \XObjectTemplate \Css \StyleMap $ style ,
156+ float $ fontSize ,
157+ ): float {
158+ $ defaultLineHeight = $ fontSize * 1.2 ;
159+ $ configuredLineHeight = $ this ->styleValue ($ style , 'line-height ' , '' );
160+
161+ if ($ configuredLineHeight === '' ) {
162+ return $ defaultLineHeight ;
163+ }
164+
165+ return max ($ defaultLineHeight , $ this ->toPoints ($ configuredLineHeight ));
166+ }
167+
145168 /**
146169 * @return array{top: float, right: float, bottom: float, left: float}
147170 */
148171 private function parseBoxSpacing (string $ value ): array
149172 {
150- $ tokens = preg_split ('/\s +/ ' , trim ( $ value) );
151- $ tokens = array_values ( array_filter ( $ tokens ?: [], static fn ( string $ token ): bool => $ token !== '' )) ;
173+ preg_match_all ('/\S +/ ' , $ value, $ matches );
174+ $ tokens = $ matches [ 0 ] ;
152175
153176 if ($ tokens === []) {
154177 return ['top ' => 0.0 , 'right ' => 0.0 , 'bottom ' => 0.0 , 'left ' => 0.0 ];
@@ -174,7 +197,7 @@ private function parseBoxSpacing(string $value): array
174197
175198 private function resolveFontAlias (string $ fontFamily , string $ fontWeight ): string
176199 {
177- $ primary = strtolower (trim ( explode (', ' , $ fontFamily )[0 ], " \t\n\r\0\x0B ' \"" ) );
200+ $ primary = strtolower (explode (', ' , $ fontFamily )[0 ]);
178201 $ isBold = $ this ->isBoldWeight ($ fontWeight );
179202
180203 if (str_contains ($ primary , 'times ' )) {
@@ -190,13 +213,13 @@ private function resolveFontAlias(string $fontFamily, string $fontWeight): strin
190213
191214 private function isBoldWeight (string $ fontWeight ): bool
192215 {
193- $ normalized = strtolower (trim ( $ fontWeight) );
216+ $ normalized = strtolower ($ fontWeight );
194217 if ($ normalized === 'bold ' || $ normalized === 'bolder ' ) {
195218 return true ;
196219 }
197220
198221 if (is_numeric ($ normalized )) {
199- return ( int ) $ normalized >= 600 ;
222+ return $ normalized >= 600 ;
200223 }
201224
202225 return false ;
0 commit comments