1
1
package com .itextpdf .layout .renderer ;
2
2
3
+ import com .itextpdf .io .font .otf .Glyph ;
4
+ import com .itextpdf .io .font .otf .GlyphLine ;
5
+ import com .itextpdf .io .util .Utilities ;
3
6
import com .itextpdf .kernel .geom .Rectangle ;
4
7
import com .itextpdf .layout .Property ;
5
8
import com .itextpdf .layout .element .TabStop ;
11
14
12
15
import java .util .ArrayList ;
13
16
import java .util .Iterator ;
17
+ import java .util .List ;
14
18
import java .util .Map ;
15
19
import java .util .NavigableMap ;
16
20
@@ -19,6 +23,8 @@ public class LineRenderer extends AbstractRenderer {
19
23
protected float maxAscent ;
20
24
protected float maxDescent ;
21
25
26
+ protected byte [] levels ;
27
+
22
28
@ Override
23
29
public LineLayoutResult layout (LayoutContext layoutContext ) {
24
30
Rectangle layoutBox = layoutContext .getArea ().getBBox ().clone ();
@@ -29,9 +35,34 @@ public LineLayoutResult layout(LayoutContext layoutContext) {
29
35
maxDescent = 0 ;
30
36
int childPos = 0 ;
31
37
38
+ Property .BaseDirection baseDirection = getProperty (Property .BASE_DIRECTION );
39
+ if (levels == null && baseDirection != Property .BaseDirection .NO_BIDI ) {
40
+ for (IRenderer renderer : childRenderers ) {
41
+ if (renderer instanceof TextRenderer ) {
42
+ ((TextRenderer ) renderer ).applyOtf ();
43
+ }
44
+ }
45
+ List <Integer > unicodeIdsLst = new ArrayList <>();
46
+ for (IRenderer child : childRenderers ) {
47
+ if (child instanceof TextRenderer ) {
48
+ GlyphLine text = ((TextRenderer ) child ).getText ();
49
+ for (int i = text .start ; i < text .end ; i ++) {
50
+ assert text .glyphs .get (i ).getChars ().length > 0 ;
51
+ // we assume all the chars will have the same bidi group
52
+ // we also assume pairing symbols won't get merged with other ones
53
+ int unicode = text .glyphs .get (i ).getChars ()[0 ];
54
+ unicodeIdsLst .add (unicode );
55
+ }
56
+ }
57
+ }
58
+ levels = TypographyUtils .getBidiLevels (baseDirection , Utilities .toArray (unicodeIdsLst ));
59
+ }
60
+
32
61
boolean anythingPlaced = false ;
33
62
TabStop nextTabStop = null ;
34
63
64
+ LineLayoutResult result = null ;
65
+
35
66
while (childPos < childRenderers .size ()) {
36
67
IRenderer childRenderer = childRenderers .get (childPos );
37
68
LayoutResult childResult ;
@@ -125,23 +156,86 @@ public LineLayoutResult layout(LayoutContext layoutContext) {
125
156
split [1 ].childRenderers .addAll (childRenderers .subList (childPos + 1 , childRenderers .size ()));
126
157
}
127
158
128
- split [0 ].adjustChildrenYLine ().trimLast ();
129
- LineLayoutResult result = new LineLayoutResult (anythingPlaced ? LayoutResult .PARTIAL : LayoutResult .NOTHING , occupiedArea , split [0 ], split [1 ]);
159
+ result = new LineLayoutResult (anythingPlaced ? LayoutResult .PARTIAL : LayoutResult .NOTHING , occupiedArea , split [0 ], split [1 ]);
130
160
if (childResult .getStatus () == LayoutResult .PARTIAL && childResult instanceof TextLayoutResult && ((TextLayoutResult ) childResult ).isSplitForcedByNewline ())
131
161
result .setSplitForcedByNewline (true );
132
- return result ;
162
+ break ;
133
163
} else {
134
164
anythingPlaced = true ;
135
165
childPos ++;
136
166
}
137
167
}
138
168
169
+ if (result == null ) {
170
+ if (anythingPlaced ) {
171
+ result = new LineLayoutResult (LayoutResult .FULL , occupiedArea , null , null );
172
+ } else {
173
+ result = new LineLayoutResult (LayoutResult .NOTHING , occupiedArea , null , this );
174
+ }
175
+ }
176
+
177
+ // Consider for now that all the children have the same font, and that after reordering text pieces
178
+ // can be reordered, but cannot be split.
179
+ if (baseDirection != Property .BaseDirection .NO_BIDI ) {
180
+ List <IRenderer > children = null ;
181
+ if (result .getStatus () == LayoutResult .PARTIAL ) {
182
+ children = result .getSplitRenderer ().getChildRenderers ();
183
+ } else if (result .getStatus () == LayoutResult .FULL ) {
184
+ children = getChildRenderers ();
185
+ }
186
+
187
+ if (children != null ) {
188
+ List <RendererGlyph > lineGlyphs = new ArrayList <>();
189
+ for (IRenderer child : children ) {
190
+ if (child instanceof TextRenderer ) {
191
+ GlyphLine childLine = ((TextRenderer ) child ).line ;
192
+ for (int i = childLine .start ; i < childLine .end ; i ++) {
193
+ lineGlyphs .add (new RendererGlyph (childLine .get (i ), (TextRenderer ) child ));
194
+ }
195
+ }
196
+ }
197
+ byte [] lineLevels = new byte [lineGlyphs .size ()];
198
+ System .arraycopy (levels , 0 , lineLevels , 0 , lineGlyphs .size ());
199
+ List <RendererGlyph > reorderedLine = TypographyUtils .reoderLine (lineGlyphs , lineLevels , levels );
200
+
201
+ if (reorderedLine != null ) {
202
+ children .clear ();
203
+ int pos = 0 ;
204
+ while (pos < reorderedLine .size ()) {
205
+ IRenderer renderer = reorderedLine .get (pos ).renderer ;
206
+ children .add (new TextRenderer ((TextRenderer ) renderer ));
207
+ ((TextRenderer ) children .get (children .size () - 1 )).line = new GlyphLine (((TextRenderer ) children .get (children .size () - 1 )).line );
208
+ GlyphLine gl = ((TextRenderer ) children .get (children .size () - 1 )).line ;
209
+ gl .end = gl .start ;
210
+ while (pos < reorderedLine .size () && reorderedLine .get (pos ).renderer == renderer ) {
211
+ gl .set (gl .end , reorderedLine .get (pos ).glyph );
212
+ gl .end ++;
213
+ pos ++;
214
+ }
215
+ }
216
+
217
+ float currentXPos = layoutContext .getArea ().getBBox ().getLeft ();
218
+ for (IRenderer child : children ) {
219
+ float currentWidth = ((TextRenderer ) child ).calculateLineWidth ();
220
+ ((TextRenderer ) child ).occupiedArea .getBBox ().setX (currentXPos ).setWidth (currentWidth );
221
+ currentXPos += currentWidth ;
222
+ }
223
+ }
224
+
225
+ if (result .getStatus () == LayoutResult .PARTIAL ) {
226
+ LineRenderer overflow = (LineRenderer ) result .getOverflowRenderer ();
227
+ overflow .levels = new byte [levels .length - lineLevels .length ];
228
+ System .arraycopy (levels , lineLevels .length , overflow .levels , 0 , overflow .levels .length );
229
+ }
230
+ }
231
+ }
232
+
139
233
if (anythingPlaced ) {
140
- adjustChildrenYLine ().trimLast ();
141
- return new LineLayoutResult (LayoutResult .FULL , occupiedArea , null , null );
142
- } else {
143
- return new LineLayoutResult (LayoutResult .NOTHING , occupiedArea , null , this );
234
+ LineRenderer processed = result .getStatus () == LayoutResult .FULL ? this : (LineRenderer ) result .getSplitRenderer ();
235
+ processed .adjustChildrenYLine ().trimLast ();
144
236
}
237
+
238
+ return result ;
145
239
}
146
240
147
241
public float getMaxAscent () {
@@ -268,9 +362,11 @@ protected LineRenderer[] split() {
268
362
splitRenderer .parent = parent ;
269
363
splitRenderer .maxAscent = maxAscent ;
270
364
splitRenderer .maxDescent = maxDescent ;
365
+ splitRenderer .levels = levels ;
271
366
272
367
LineRenderer overflowRenderer = createOverflowRenderer ();
273
368
overflowRenderer .parent = parent ;
369
+ overflowRenderer .levels = levels ;
274
370
275
371
return new LineRenderer [] {splitRenderer , overflowRenderer };
276
372
}
@@ -355,7 +451,6 @@ private TabStop calculateTab(IRenderer childRenderer, float curWidth, float line
355
451
* Returns resulting width of the tab.
356
452
*/
357
453
private float calculateTab (Rectangle layoutBox , float curWidth , TabStop tabStop , IRenderer nextElementRenderer , LayoutResult nextElementResult , IRenderer tabRenderer ) {
358
-
359
454
float childWidth = 0 ;
360
455
if (nextElementRenderer != null )
361
456
childWidth = nextElementRenderer .getOccupiedArea ().getBBox ().getWidth ();
@@ -394,4 +489,15 @@ private void processDefaultTab(IRenderer tabRenderer, float curWidth, float line
394
489
tabRenderer .setProperty (Property .WIDTH , Property .UnitValue .createPointValue (tabWidth ));
395
490
tabRenderer .setProperty (Property .HEIGHT , maxAscent - maxDescent );
396
491
}
492
+
493
+ static class RendererGlyph {
494
+ public RendererGlyph (Glyph glyph , TextRenderer textRenderer ) {
495
+ this .glyph = glyph ;
496
+ this .renderer = textRenderer ;
497
+ }
498
+
499
+ public Glyph glyph ;
500
+ public TextRenderer renderer ;
501
+ }
502
+
397
503
}
0 commit comments