1 /*****************************************************************************
2 * text_layout.c : Text shaping and layout
3 *****************************************************************************
4 * Copyright (C) 2015 VLC authors and VideoLAN
7 * Authors: Salah-Eddin Shaban <salshaaban@gmail.com>
8 * Laurent Aimar <fenrir@videolan.org>
9 * Sigmund Augdal Helberg <dnumgis@videolan.org>
10 * Gildas Bazin <gbazin@videolan.org>
11 * Jean-Baptiste Kempf <jb@videolan.org>
12 * Naohiro Koriyama <nkoriyama@gmail.com>
13 * David Fuhrmann <dfuhrmann@videolan.org>
14 * Erwan Tulou <erwan10@videolan.org>
15 * Devin Heitmueller <dheitmueller@kernellabs.com>
17 * This program is free software; you can redistribute it and/or modify it
18 * under the terms of the GNU Lesser General Public License as published by
19 * the Free Software Foundation; either version 2.1 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU Lesser General Public License for more details.
27 * You should have received a copy of the GNU Lesser General Public License
28 * along with this program; if not, write to the Free Software Foundation, Inc.,
29 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
30 *****************************************************************************/
35 * Text shaping and layout
42 #include <vlc_common.h>
43 #include <vlc_filter.h>
44 #include <vlc_text_style.h>
48 #include FT_FREETYPE_H
51 #include FT_SYNTHESIS_H
54 #if defined(HAVE_FRIBIDI)
55 # define FRIBIDI_NO_DEPRECATED 1
60 #if defined(HAVE_HARFBUZZ)
66 #include "text_layout.h"
67 #include "platform_fonts.h"
71 # undef HAVE_FONTCONFIG
72 # define HAVE_FONT_FALLBACK
76 #ifdef HAVE_FONTCONFIG
77 # define HAVE_FONT_FALLBACK
82 # define HAVE_FONT_FALLBACK
87 # define HAVE_FONT_FALLBACK
90 #ifndef HAVE_FONT_FALLBACK
91 # warning YOU ARE MISSING FONTS FALLBACK. TEXT WILL BE INCORRECT
95 * Within a paragraph, run_desc_t represents a run of characters
96 * having the same font face, size, and style, Unicode script
99 typedef struct run_desc_t
104 const text_style_t
*p_style
;
108 hb_direction_t direction
;
109 hb_font_t
*p_hb_font
;
110 hb_buffer_t
*p_buffer
;
111 hb_glyph_info_t
*p_glyph_infos
;
112 hb_glyph_position_t
*p_glyph_positions
;
113 unsigned int i_glyph_count
;
119 * Glyph bitmaps. Advance and offset are 26.6 values
121 typedef struct glyph_bitmaps_t
127 FT_BBox outline_bbox
;
135 typedef struct paragraph_t
137 uni_char_t
*p_code_points
; /**< Unicode code points */
138 int *pi_glyph_indices
; /**< Glyph index values within the run's font face */
139 text_style_t
**pp_styles
;
140 FT_Face
*pp_faces
; /**< Used to determine run boundaries when performing font fallback */
141 int *pi_run_ids
; /**< The run to which each glyph belongs */
142 glyph_bitmaps_t
*p_glyph_bitmaps
;
143 uint8_t *pi_karaoke_bar
;
150 hb_script_t
*p_scripts
;
154 FriBidiCharType
*p_types
;
155 FriBidiLevel
*p_levels
;
156 FriBidiStrIndex
*pi_reordered_indices
;
157 FriBidiParType paragraph_type
;
162 static void FreeLine( line_desc_t
*p_line
)
164 for( int i
= 0; i
< p_line
->i_character_count
; i
++ )
166 line_character_t
*ch
= &p_line
->p_character
[i
];
167 FT_Done_Glyph( (FT_Glyph
)ch
->p_glyph
);
169 FT_Done_Glyph( (FT_Glyph
)ch
->p_outline
);
171 FT_Done_Glyph( (FT_Glyph
)ch
->p_shadow
);
174 free( p_line
->p_character
);
178 void FreeLines( line_desc_t
*p_lines
)
180 for( line_desc_t
*p_line
= p_lines
; p_line
!= NULL
; )
182 line_desc_t
*p_next
= p_line
->p_next
;
188 line_desc_t
*NewLine( int i_count
)
190 line_desc_t
*p_line
= malloc( sizeof(*p_line
) );
195 p_line
->p_next
= NULL
;
197 p_line
->i_base_line
= 0;
198 p_line
->i_character_count
= 0;
199 p_line
->i_first_visible_char_index
= -1;
200 p_line
->i_last_visible_char_index
= -2;
202 p_line
->bbox
.xMin
= INT_MAX
;
203 p_line
->bbox
.yMin
= INT_MAX
;
204 p_line
->bbox
.xMax
= INT_MIN
;
205 p_line
->bbox
.yMax
= INT_MIN
;
207 p_line
->p_character
= calloc( i_count
, sizeof(*p_line
->p_character
) );
208 if( !p_line
->p_character
)
216 static void FixGlyph( FT_Glyph glyph
, FT_BBox
*p_bbox
,
217 FT_Pos i_x_advance
, FT_Pos i_y_advance
,
218 const FT_Vector
*p_pen
)
220 FT_BitmapGlyph glyph_bmp
= (FT_BitmapGlyph
)glyph
;
221 if( p_bbox
->xMin
>= p_bbox
->xMax
)
223 p_bbox
->xMin
= FT_CEIL(p_pen
->x
);
224 p_bbox
->xMax
= FT_CEIL(p_pen
->x
+ i_x_advance
);
225 glyph_bmp
->left
= p_bbox
->xMin
;
227 if( p_bbox
->yMin
>= p_bbox
->yMax
)
229 p_bbox
->yMax
= FT_CEIL(p_pen
->y
);
230 p_bbox
->yMin
= FT_CEIL(p_pen
->y
+ i_y_advance
);
231 glyph_bmp
->top
= p_bbox
->yMax
;
235 static void BBoxEnlarge( FT_BBox
*p_max
, const FT_BBox
*p
)
237 p_max
->xMin
= __MIN(p_max
->xMin
, p
->xMin
);
238 p_max
->yMin
= __MIN(p_max
->yMin
, p
->yMin
);
239 p_max
->xMax
= __MAX(p_max
->xMax
, p
->xMax
);
240 p_max
->yMax
= __MAX(p_max
->yMax
, p
->yMax
);
243 static paragraph_t
*NewParagraph( filter_t
*p_filter
,
245 const uni_char_t
*p_code_points
,
246 text_style_t
**pp_styles
,
247 uint32_t *pi_k_dates
,
250 paragraph_t
*p_paragraph
= calloc( 1, sizeof( paragraph_t
) );
254 p_paragraph
->i_size
= i_size
;
255 p_paragraph
->p_code_points
=
256 malloc( i_size
* sizeof( *p_paragraph
->p_code_points
) );
257 p_paragraph
->pi_glyph_indices
=
258 malloc( i_size
* sizeof( *p_paragraph
->pi_glyph_indices
) );
259 p_paragraph
->pp_styles
=
260 malloc( i_size
* sizeof( *p_paragraph
->pp_styles
) );
261 p_paragraph
->pp_faces
=
262 calloc( i_size
, sizeof( *p_paragraph
->pp_faces
) );
263 p_paragraph
->pi_run_ids
=
264 calloc( i_size
, sizeof( *p_paragraph
->pi_run_ids
) );
265 p_paragraph
->p_glyph_bitmaps
=
266 calloc( i_size
, sizeof( *p_paragraph
->p_glyph_bitmaps
) );
267 p_paragraph
->pi_karaoke_bar
=
268 calloc( i_size
, sizeof( *p_paragraph
->pi_karaoke_bar
) );
270 p_paragraph
->p_runs
= calloc( i_runs_size
, sizeof( run_desc_t
) );
271 p_paragraph
->i_runs_size
= i_runs_size
;
272 p_paragraph
->i_runs_count
= 0;
274 if( !p_paragraph
->p_code_points
|| !p_paragraph
->pi_glyph_indices
275 || !p_paragraph
->pp_styles
|| !p_paragraph
->pp_faces
276 || !p_paragraph
->pi_run_ids
|| !p_paragraph
->p_glyph_bitmaps
277 || !p_paragraph
->pi_karaoke_bar
|| !p_paragraph
->p_runs
)
281 memcpy( p_paragraph
->p_code_points
, p_code_points
,
282 i_size
* sizeof( *p_code_points
) );
284 memcpy( p_paragraph
->pp_styles
, pp_styles
,
285 i_size
* sizeof( *pp_styles
) );
288 int64_t i_elapsed
= var_GetInteger( p_filter
, "spu-elapsed" ) / 1000;
289 for( int i
= 0; i
< i_size
; ++i
)
291 p_paragraph
->pi_karaoke_bar
[ i
] = pi_k_dates
[ i
] >= i_elapsed
;
296 p_paragraph
->p_scripts
= malloc( i_size
* sizeof( *p_paragraph
->p_scripts
) );
297 if( !p_paragraph
->p_scripts
)
302 p_paragraph
->p_levels
= malloc( i_size
* sizeof( *p_paragraph
->p_levels
) );
303 p_paragraph
->p_types
= malloc( i_size
* sizeof( *p_paragraph
->p_types
) );
304 p_paragraph
->pi_reordered_indices
=
305 malloc( i_size
* sizeof( *p_paragraph
->pi_reordered_indices
) );
307 if( !p_paragraph
->p_levels
|| !p_paragraph
->p_types
308 || !p_paragraph
->pi_reordered_indices
)
311 int i_direction
= var_InheritInteger( p_filter
, "freetype-text-direction" );
312 if( i_direction
== 0 )
313 p_paragraph
->paragraph_type
= FRIBIDI_PAR_LTR
;
314 else if( i_direction
== 1 )
315 p_paragraph
->paragraph_type
= FRIBIDI_PAR_RTL
;
317 p_paragraph
->paragraph_type
= FRIBIDI_PAR_ON
;
323 if( p_paragraph
->p_code_points
) free( p_paragraph
->p_code_points
);
324 if( p_paragraph
->pi_glyph_indices
) free( p_paragraph
->pi_glyph_indices
);
325 if( p_paragraph
->pp_styles
) free( p_paragraph
->pp_styles
);
326 if( p_paragraph
->pp_faces
) free( p_paragraph
->pp_faces
);
327 if( p_paragraph
->pi_run_ids
) free( p_paragraph
->pi_run_ids
);
328 if( p_paragraph
->p_glyph_bitmaps
) free( p_paragraph
->p_glyph_bitmaps
);
329 if (p_paragraph
->pi_karaoke_bar
) free( p_paragraph
->pi_karaoke_bar
);
330 if( p_paragraph
->p_runs
) free( p_paragraph
->p_runs
);
332 if( p_paragraph
->p_scripts
) free( p_paragraph
->p_scripts
);
335 if( p_paragraph
->p_levels
) free( p_paragraph
->p_levels
);
336 if( p_paragraph
->p_types
) free( p_paragraph
->p_types
);
337 if( p_paragraph
->pi_reordered_indices
)
338 free( p_paragraph
->pi_reordered_indices
);
344 static void FreeParagraph( paragraph_t
*p_paragraph
)
346 free( p_paragraph
->p_runs
);
347 free( p_paragraph
->pi_glyph_indices
);
348 free( p_paragraph
->p_glyph_bitmaps
);
349 free( p_paragraph
->pi_karaoke_bar
);
350 free( p_paragraph
->pi_run_ids
);
351 free( p_paragraph
->pp_faces
);
352 free( p_paragraph
->pp_styles
);
353 free( p_paragraph
->p_code_points
);
356 free( p_paragraph
->p_scripts
);
360 free( p_paragraph
->pi_reordered_indices
);
361 free( p_paragraph
->p_types
);
362 free( p_paragraph
->p_levels
);
369 static int AnalyzeParagraph( paragraph_t
*p_paragraph
)
371 fribidi_get_bidi_types( p_paragraph
->p_code_points
,
373 p_paragraph
->p_types
);
374 fribidi_get_par_embedding_levels( p_paragraph
->p_types
,
376 &p_paragraph
->paragraph_type
,
377 p_paragraph
->p_levels
);
380 hb_unicode_funcs_t
*p_funcs
= hb_unicode_funcs_get_default();
381 for( int i
= 0; i
< p_paragraph
->i_size
; ++i
)
382 p_paragraph
->p_scripts
[ i
] =
383 hb_unicode_script( p_funcs
, p_paragraph
->p_code_points
[ i
] );
385 hb_script_t i_last_script
;
386 int i_last_script_index
= -1;
387 int i_last_set_index
= -1;
390 * For shaping to work, characters that are assigned HB_SCRIPT_COMMON or
391 * HB_SCRIPT_INHERITED should be resolved to the last encountered valid
392 * script value, if any, and to the first one following them otherwise
394 for( int i
= 0; i
< p_paragraph
->i_size
; ++i
)
396 if( p_paragraph
->p_scripts
[ i
] == HB_SCRIPT_COMMON
397 || p_paragraph
->p_scripts
[ i
] == HB_SCRIPT_INHERITED
)
399 if( i_last_script_index
!= -1)
401 p_paragraph
->p_scripts
[ i
] = i_last_script
;
402 i_last_set_index
= i
;
407 for( int j
= i_last_set_index
+ 1; j
< i
; ++j
)
408 p_paragraph
->p_scripts
[ j
] = p_paragraph
->p_scripts
[ i
];
410 i_last_script
= p_paragraph
->p_scripts
[ i
];
411 i_last_script_index
= i
;
412 i_last_set_index
= i
;
415 #endif //HAVE_HARFBUZZ
419 #endif //HAVE_FRIBIDI
421 static int AddRun( filter_t
*p_filter
,
422 paragraph_t
*p_paragraph
,
426 const text_style_t
*p_style
)
428 if( i_start_offset
>= i_end_offset
429 || i_start_offset
< 0 || i_start_offset
>= p_paragraph
->i_size
430 || i_end_offset
<= 0 || i_end_offset
> p_paragraph
->i_size
)
433 "AddRun() invalid parameters. Paragraph size: %d, "
434 "Start offset: %d, End offset: %d",
435 p_paragraph
->i_size
, i_start_offset
, i_end_offset
);
439 if( p_paragraph
->i_runs_count
== p_paragraph
->i_runs_size
)
441 run_desc_t
*p_new_runs
=
442 realloc( p_paragraph
->p_runs
,
443 p_paragraph
->i_runs_size
* 2 * sizeof( *p_new_runs
) );
447 memset( p_new_runs
+ p_paragraph
->i_runs_size
, 0,
448 p_paragraph
->i_runs_size
* sizeof( *p_new_runs
) );
450 p_paragraph
->p_runs
= p_new_runs
;
451 p_paragraph
->i_runs_size
*= 2;
454 int i_run_id
= p_paragraph
->i_runs_count
;
455 run_desc_t
*p_run
= p_paragraph
->p_runs
+ p_paragraph
->i_runs_count
++;
456 p_run
->i_start_offset
= i_start_offset
;
457 p_run
->i_end_offset
= i_end_offset
;
458 p_run
->p_face
= p_face
;
461 p_run
->p_style
= p_style
;
463 p_run
->p_style
= p_paragraph
->pp_styles
[ i_start_offset
];
466 p_run
->script
= p_paragraph
->p_scripts
[ i_start_offset
];
467 p_run
->direction
= p_paragraph
->p_levels
[ i_start_offset
] & 1 ?
468 HB_DIRECTION_RTL
: HB_DIRECTION_LTR
;
471 for( int i
= i_start_offset
; i
< i_end_offset
; ++i
)
472 p_paragraph
->pi_run_ids
[ i
] = i_run_id
;
477 #ifdef HAVE_FONT_FALLBACK
479 * Add a run with font fallback, possibly breaking the run further
480 * into runs of glyphs that end up having the same font face.
482 static int AddRunWithFallback( filter_t
*p_filter
, paragraph_t
*p_paragraph
,
483 int i_start_offset
, int i_end_offset
)
485 if( i_start_offset
>= i_end_offset
486 || i_start_offset
< 0 || i_start_offset
>= p_paragraph
->i_size
487 || i_end_offset
<= 0 || i_end_offset
> p_paragraph
->i_size
)
490 "AddRunWithFallback() invalid parameters. Paragraph size: %d, "
491 "Start offset: %d, End offset: %d",
492 p_paragraph
->i_size
, i_start_offset
, i_end_offset
);
496 const text_style_t
*p_style
= p_paragraph
->pp_styles
[ i_start_offset
];
498 /* Maximum number of faces to try for each run */
500 FT_Face pp_faces
[ MAX_FACES
] = {0};
502 pp_faces
[ 0 ] = SelectAndLoadFace( p_filter
, p_style
, 0 );
504 for( int i
= i_start_offset
; i
< i_end_offset
; ++i
)
507 int i_glyph_index
= 0;
508 FT_Face p_face
= NULL
;
510 p_face
= pp_faces
[ i_index
];
512 p_face
= pp_faces
[ i_index
] =
513 SelectAndLoadFace( p_filter
, p_style
,
514 p_paragraph
->p_code_points
[ i
] );
517 i_glyph_index
= FT_Get_Char_Index( p_face
,
518 p_paragraph
->p_code_points
[ i
] );
521 p_paragraph
->pp_faces
[ i
] = p_face
;
524 * Move p_face to the beginning of the array. Otherwise strikethrough
525 * lines can appear segmented, being rendered at a certain height
526 * through spaces and at a different height through words.
527 * Skip this step for the specified special characters. See #15840.
531 uni_char_t codepoint
= p_paragraph
->p_code_points
[ i
];
532 if( codepoint
!= 0x0009 && codepoint
!= 0x00A0
533 && codepoint
!= 0x1680 && codepoint
!= 0x061C
534 && codepoint
!= 0x202F && codepoint
!= 0x205F
535 && codepoint
!= 0x3000 && codepoint
!= 0xFEFF
536 && !( codepoint
>= 0x2000 && codepoint
<= 0x200F )
537 && !( codepoint
>= 0x202A && codepoint
<= 0x202E )
538 && !( codepoint
>= 0x2060 && codepoint
<= 0x2069 ) )
540 pp_faces
[ i_index
] = pp_faces
[ 0 ];
541 pp_faces
[ 0 ] = p_face
;
546 } while( i_glyph_index
== 0 && ++i_index
< MAX_FACES
);
549 int i_run_start
= i_start_offset
;
550 for( int i
= i_start_offset
; i
<= i_end_offset
; ++i
)
552 if( i
== i_end_offset
553 || p_paragraph
->pp_faces
[ i_run_start
] != p_paragraph
->pp_faces
[ i
] )
555 if( AddRun( p_filter
, p_paragraph
, i_run_start
, i
,
556 p_paragraph
->pp_faces
[ i_run_start
], NULL
) )
567 static bool FaceStyleEquals( filter_t
*p_filter
, const text_style_t
*p_style1
,
568 const text_style_t
*p_style2
)
570 if( !p_style1
|| !p_style2
)
572 if( p_style1
== p_style2
)
575 const int i_style_mask
= STYLE_BOLD
| STYLE_ITALIC
| STYLE_HALFWIDTH
| STYLE_DOUBLEWIDTH
;
577 const char *psz_fontname1
= p_style1
->i_style_flags
& STYLE_MONOSPACED
578 ? p_style1
->psz_monofontname
: p_style1
->psz_fontname
;
580 const char *psz_fontname2
= p_style2
->i_style_flags
& STYLE_MONOSPACED
581 ? p_style2
->psz_monofontname
: p_style2
->psz_fontname
;
583 const int i_size1
= ConvertToLiveSize( p_filter
, p_style1
);
584 const int i_size2
= ConvertToLiveSize( p_filter
, p_style2
);
586 return (p_style1
->i_style_flags
& i_style_mask
) == (p_style2
->i_style_flags
& i_style_mask
)
587 && i_size1
== i_size2
588 && !strcasecmp( psz_fontname1
, psz_fontname2
);
592 * Segment a paragraph into runs
594 static int ItemizeParagraph( filter_t
*p_filter
, paragraph_t
*p_paragraph
)
596 if( p_paragraph
->i_size
<= 0 )
599 "ItemizeParagraph() invalid parameters. Paragraph size: %d",
600 p_paragraph
->i_size
);
604 int i_last_run_start
= 0;
605 const text_style_t
*p_last_style
= p_paragraph
->pp_styles
[ 0 ];
608 hb_script_t last_script
= p_paragraph
->p_scripts
[ 0 ];
609 FriBidiLevel last_level
= p_paragraph
->p_levels
[ 0 ];
612 for( int i
= 0; i
<= p_paragraph
->i_size
; ++i
)
614 if( i
== p_paragraph
->i_size
616 || last_script
!= p_paragraph
->p_scripts
[ i
]
617 || last_level
!= p_paragraph
->p_levels
[ i
]
619 || !FaceStyleEquals( p_filter
, p_last_style
, p_paragraph
->pp_styles
[ i
] ) )
622 #ifdef HAVE_FONT_FALLBACK
623 i_ret
= AddRunWithFallback( p_filter
, p_paragraph
, i_last_run_start
, i
);
625 i_ret
= AddRun( p_filter
, p_paragraph
, i_last_run_start
, i
, NULL
, NULL
);
630 if( i
< p_paragraph
->i_size
)
632 i_last_run_start
= i
;
633 p_last_style
= p_paragraph
->pp_styles
[ i
];
635 last_script
= p_paragraph
->p_scripts
[ i
];
636 last_level
= p_paragraph
->p_levels
[ i
];
646 * Shape an itemized paragraph using HarfBuzz.
647 * This is where the glyphs of complex scripts get their positions
648 * (offsets and advance values) and final forms.
649 * Glyph substitutions of base glyphs and diacritics may take place,
650 * so the paragraph size may change.
652 static int ShapeParagraphHarfBuzz( filter_t
*p_filter
,
653 paragraph_t
**p_old_paragraph
)
655 paragraph_t
*p_paragraph
= *p_old_paragraph
;
656 paragraph_t
*p_new_paragraph
= 0;
657 filter_sys_t
*p_sys
= p_filter
->p_sys
;
658 int i_total_glyphs
= 0;
659 int i_ret
= VLC_EGENERIC
;
661 if( p_paragraph
->i_size
<= 0 || p_paragraph
->i_runs_count
<= 0 )
663 msg_Err( p_filter
, "ShapeParagraphHarfBuzz() invalid parameters. "
664 "Paragraph size: %d. Runs count %d",
665 p_paragraph
->i_size
, p_paragraph
->i_runs_count
);
669 for( int i
= 0; i
< p_paragraph
->i_runs_count
; ++i
)
671 run_desc_t
*p_run
= p_paragraph
->p_runs
+ i
;
672 const text_style_t
*p_style
= p_run
->p_style
;
675 * With HarfBuzz and no font fallback, this is where font faces
676 * are loaded. In the other two paths (shaping with FriBidi or no
677 * shaping at all), faces are loaded in LoadGlyphs().
679 * If we have font fallback, font faces in all paths will be
680 * loaded in AddRunWithFallback(), except for runs of codepoints
681 * for which no font could be found.
686 p_face
= SelectAndLoadFace( p_filter
, p_style
, 0 );
689 p_face
= p_sys
->p_face
;
690 p_style
= p_sys
->p_default_style
;
691 p_run
->p_style
= p_style
;
693 p_run
->p_face
= p_face
;
696 p_face
= p_run
->p_face
;
698 p_run
->p_hb_font
= hb_ft_font_create( p_face
, 0 );
699 if( !p_run
->p_hb_font
)
702 "ShapeParagraphHarfBuzz(): hb_ft_font_create() error" );
706 p_run
->p_buffer
= hb_buffer_create();
707 if( !p_run
->p_buffer
)
710 "ShapeParagraphHarfBuzz(): hb_buffer_create() error" );
714 hb_buffer_set_direction( p_run
->p_buffer
, p_run
->direction
);
715 hb_buffer_set_script( p_run
->p_buffer
, p_run
->script
);
717 hb_buffer_add_utf16( p_run
->p_buffer
,
718 p_paragraph
->p_code_points
+ p_run
->i_start_offset
,
719 p_run
->i_end_offset
- p_run
->i_start_offset
, 0,
720 p_run
->i_end_offset
- p_run
->i_start_offset
);
722 hb_buffer_add_utf32( p_run
->p_buffer
,
723 p_paragraph
->p_code_points
+ p_run
->i_start_offset
,
724 p_run
->i_end_offset
- p_run
->i_start_offset
, 0,
725 p_run
->i_end_offset
- p_run
->i_start_offset
);
727 hb_shape( p_run
->p_hb_font
, p_run
->p_buffer
, 0, 0 );
728 p_run
->p_glyph_infos
=
729 hb_buffer_get_glyph_infos( p_run
->p_buffer
, &p_run
->i_glyph_count
);
730 p_run
->p_glyph_positions
=
731 hb_buffer_get_glyph_positions( p_run
->p_buffer
, &p_run
->i_glyph_count
);
733 if( p_run
->i_glyph_count
<= 0 )
736 "ShapeParagraphHarfBuzz() invalid glyph count in shaped run" );
740 i_total_glyphs
+= p_run
->i_glyph_count
;
743 p_new_paragraph
= NewParagraph( p_filter
, i_total_glyphs
, 0, 0, 0,
744 p_paragraph
->i_runs_size
);
745 if( !p_new_paragraph
)
750 p_new_paragraph
->paragraph_type
= p_paragraph
->paragraph_type
;
753 for( int i
= 0; i
< p_paragraph
->i_runs_count
; ++i
)
755 run_desc_t
*p_run
= p_paragraph
->p_runs
+ i
;
756 hb_glyph_info_t
*p_infos
= p_run
->p_glyph_infos
;
757 hb_glyph_position_t
*p_positions
= p_run
->p_glyph_positions
;
758 for( unsigned int j
= 0; j
< p_run
->i_glyph_count
; ++j
)
761 * HarfBuzz reverses the order of glyphs in RTL runs. We reverse
762 * it again here to keep the glyphs in their logical order.
763 * For line breaking of paragraphs to work correctly, visual
764 * reordering should be done after line breaking has taken
767 int i_run_index
= p_run
->direction
== HB_DIRECTION_LTR
?
768 j
: p_run
->i_glyph_count
- 1 - j
;
770 p_infos
[ i_run_index
].cluster
+ p_run
->i_start_offset
;
772 p_new_paragraph
->p_code_points
[ i_index
] = 0;
773 p_new_paragraph
->pi_glyph_indices
[ i_index
] =
774 p_infos
[ i_run_index
].codepoint
;
775 p_new_paragraph
->p_scripts
[ i_index
] =
776 p_paragraph
->p_scripts
[ i_source_index
];
777 p_new_paragraph
->p_types
[ i_index
] =
778 p_paragraph
->p_types
[ i_source_index
];
779 p_new_paragraph
->p_levels
[ i_index
] =
780 p_paragraph
->p_levels
[ i_source_index
];
781 p_new_paragraph
->pp_styles
[ i_index
] =
782 p_paragraph
->pp_styles
[ i_source_index
];
783 p_new_paragraph
->pi_karaoke_bar
[ i_index
] =
784 p_paragraph
->pi_karaoke_bar
[ i_source_index
];
785 p_new_paragraph
->p_glyph_bitmaps
[ i_index
].i_x_offset
=
786 p_positions
[ i_run_index
].x_offset
;
787 p_new_paragraph
->p_glyph_bitmaps
[ i_index
].i_y_offset
=
788 p_positions
[ i_run_index
].y_offset
;
789 p_new_paragraph
->p_glyph_bitmaps
[ i_index
].i_x_advance
=
790 p_positions
[ i_run_index
].x_advance
;
791 p_new_paragraph
->p_glyph_bitmaps
[ i_index
].i_y_advance
=
792 p_positions
[ i_run_index
].y_advance
;
796 if( AddRun( p_filter
, p_new_paragraph
, i_index
- p_run
->i_glyph_count
,
797 i_index
, p_run
->p_face
, p_run
->p_style
) )
801 for( int i
= 0; i
< p_paragraph
->i_runs_count
; ++i
)
803 hb_font_destroy( p_paragraph
->p_runs
[ i
].p_hb_font
);
804 hb_buffer_destroy( p_paragraph
->p_runs
[ i
].p_buffer
);
806 FreeParagraph( *p_old_paragraph
);
807 *p_old_paragraph
= p_new_paragraph
;
812 for( int i
= 0; i
< p_paragraph
->i_runs_count
; ++i
)
814 if( p_paragraph
->p_runs
[ i
].p_hb_font
)
815 hb_font_destroy( p_paragraph
->p_runs
[ i
].p_hb_font
);
816 if( p_paragraph
->p_runs
[ i
].p_buffer
)
817 hb_buffer_destroy( p_paragraph
->p_runs
[ i
].p_buffer
);
820 if( p_new_paragraph
)
821 FreeParagraph( p_new_paragraph
);
828 #ifndef HAVE_HARFBUZZ
830 * Shape a paragraph with FriBidi.
831 * Shaping with FriBidi is currently limited to mirroring and simple
834 static int ShapeParagraphFriBidi( filter_t
*p_filter
, paragraph_t
*p_paragraph
)
837 if( p_paragraph
->i_size
<= 0 )
840 "ShapeParagraphFriBidi() invalid parameters. Paragraph size: %d",
841 p_paragraph
->i_size
);
845 FriBidiJoiningType
*p_joining_types
=
846 malloc( p_paragraph
->i_size
* sizeof( *p_joining_types
) );
847 if( !p_joining_types
)
850 fribidi_get_joining_types( p_paragraph
->p_code_points
,
851 p_paragraph
->i_size
, p_joining_types
);
852 fribidi_join_arabic( p_paragraph
->p_types
, p_paragraph
->i_size
,
853 p_paragraph
->p_levels
, p_joining_types
);
854 fribidi_shape( FRIBIDI_FLAGS_DEFAULT
| FRIBIDI_FLAGS_ARABIC
,
855 p_paragraph
->p_levels
,
858 p_paragraph
->p_code_points
);
860 free( p_joining_types
);
866 * Zero-width invisible characters include Unicode control characters and
867 * zero-width spaces among other things. If not removed they can show up in the
868 * text as squares or other glyphs depending on the font. Zero-width spaces are
869 * inserted when shaping with FriBidi, when it performs glyph substitution for
872 static int RemoveZeroWidthCharacters( paragraph_t
*p_paragraph
)
874 for( int i
= 0; i
< p_paragraph
->i_size
; ++i
)
876 uni_char_t ch
= p_paragraph
->p_code_points
[ i
];
879 || ( ch
>= 0x202a && ch
<= 0x202e )
880 || ( ch
>= 0x2060 && ch
<= 0x2069 )
881 || ( ch
>= 0x200b && ch
<= 0x200f ) )
883 glyph_bitmaps_t
*p_bitmaps
= p_paragraph
->p_glyph_bitmaps
+ i
;
884 if( p_bitmaps
->p_glyph
)
885 FT_Done_Glyph( p_bitmaps
->p_glyph
);
886 if( p_bitmaps
->p_outline
)
887 FT_Done_Glyph( p_bitmaps
->p_outline
);
888 p_bitmaps
->p_glyph
= 0;
889 p_bitmaps
->p_outline
= 0;
890 p_bitmaps
->p_shadow
= 0;
891 p_bitmaps
->i_x_advance
= 0;
892 p_bitmaps
->i_y_advance
= 0;
900 * Set advance values of non-spacing marks to zero. Diacritics are
901 * not positioned correctly but the text is more readable.
902 * For full shaping HarfBuzz is required.
904 static int ZeroNsmAdvance( paragraph_t
*p_paragraph
)
906 for( int i
= 0; i
< p_paragraph
->i_size
; ++i
)
907 if( p_paragraph
->p_types
[ i
] == FRIBIDI_TYPE_NSM
)
909 p_paragraph
->p_glyph_bitmaps
[ i
].i_x_advance
= 0;
910 p_paragraph
->p_glyph_bitmaps
[ i
].i_y_advance
= 0;
918 * Load the glyphs of a paragraph. When shaping with HarfBuzz the glyph indices
919 * have already been determined at this point, as well as the advance values.
921 static int LoadGlyphs( filter_t
*p_filter
, paragraph_t
*p_paragraph
,
922 bool b_use_glyph_indices
, bool b_overwrite_advance
,
923 unsigned *pi_max_advance_x
)
925 if( p_paragraph
->i_size
<= 0 || p_paragraph
->i_runs_count
<= 0 )
927 msg_Err( p_filter
, "LoadGlyphs() invalid parameters. "
928 "Paragraph size: %d. Runs count %d", p_paragraph
->i_size
,
929 p_paragraph
->i_runs_count
);
933 filter_sys_t
*p_sys
= p_filter
->p_sys
;
934 *pi_max_advance_x
= 0;
936 for( int i
= 0; i
< p_paragraph
->i_runs_count
; ++i
)
938 run_desc_t
*p_run
= p_paragraph
->p_runs
+ i
;
939 const text_style_t
*p_style
= p_run
->p_style
;
940 const int i_live_size
= ConvertToLiveSize( p_filter
, p_style
);
945 p_face
= SelectAndLoadFace( p_filter
, p_style
, 0 );
948 /* Uses the default font and style */
949 p_face
= p_sys
->p_face
;
950 p_style
= p_sys
->p_default_style
;
951 p_run
->p_style
= p_style
;
953 p_run
->p_face
= p_face
;
956 p_face
= p_run
->p_face
;
958 if( p_sys
->p_stroker
&& (p_style
->i_style_flags
& STYLE_OUTLINE
) )
960 double f_outline_thickness
=
961 var_InheritInteger( p_filter
, "freetype-outline-thickness" ) / 100.0;
962 f_outline_thickness
= VLC_CLIP( f_outline_thickness
, 0.0, 0.5 );
963 int i_radius
= ( i_live_size
<< 6 ) * f_outline_thickness
;
964 FT_Stroker_Set( p_sys
->p_stroker
,
966 FT_STROKER_LINECAP_ROUND
,
967 FT_STROKER_LINEJOIN_ROUND
, 0 );
970 for( int j
= p_run
->i_start_offset
; j
< p_run
->i_end_offset
; ++j
)
973 if( b_use_glyph_indices
)
974 i_glyph_index
= p_paragraph
->pi_glyph_indices
[ j
];
977 FT_Get_Char_Index( p_face
, p_paragraph
->p_code_points
[ j
] );
979 glyph_bitmaps_t
*p_bitmaps
= p_paragraph
->p_glyph_bitmaps
+ j
;
981 #define SKIP_GLYPH( p_bitmaps ) \
983 p_bitmaps->p_glyph = 0; \
984 p_bitmaps->p_outline = 0; \
985 p_bitmaps->p_shadow = 0; \
986 p_bitmaps->i_x_advance = 0; \
987 p_bitmaps->i_y_advance = 0; \
993 uni_char_t codepoint
= p_paragraph
->p_code_points
[ j
];
995 * If the font has no support for special space characters, use regular
996 * space glyphs instead of the .notdef glyph.
998 if( codepoint
== 0x0009 || codepoint
== 0x00A0
999 || codepoint
== 0x1680 || codepoint
== 0x3000
1000 || codepoint
== 0x202F || codepoint
== 0x205F
1001 || ( codepoint
>= 0x2000 && codepoint
<= 0x200A )
1003 || p_paragraph
->p_types
[ j
] == FRIBIDI_TYPE_WS
1004 || p_paragraph
->p_types
[ j
] == FRIBIDI_TYPE_CS
1005 || p_paragraph
->p_types
[ j
] == FRIBIDI_TYPE_SS
1011 /* Skip carriage returns */
1012 else if( codepoint
== 0x0D
1014 || p_paragraph
->p_types
[ j
] == FRIBIDI_TYPE_BS
1017 SKIP_GLYPH( p_bitmaps
)
1020 if( FT_Load_Glyph( p_face
, i_glyph_index
,
1021 FT_LOAD_NO_BITMAP
| FT_LOAD_DEFAULT
)
1022 && FT_Load_Glyph( p_face
, i_glyph_index
, FT_LOAD_DEFAULT
) )
1023 SKIP_GLYPH( p_bitmaps
)
1025 if( ( p_style
->i_style_flags
& STYLE_BOLD
)
1026 && !( p_face
->style_flags
& FT_STYLE_FLAG_BOLD
) )
1027 FT_GlyphSlot_Embolden( p_face
->glyph
);
1028 if( ( p_style
->i_style_flags
& STYLE_ITALIC
)
1029 && !( p_face
->style_flags
& FT_STYLE_FLAG_ITALIC
) )
1030 FT_GlyphSlot_Oblique( p_face
->glyph
);
1032 if( FT_Get_Glyph( p_face
->glyph
, &p_bitmaps
->p_glyph
) )
1033 SKIP_GLYPH( p_bitmaps
)
1037 if( p_filter
->p_sys
->p_stroker
&& (p_style
->i_style_flags
& STYLE_OUTLINE
) )
1039 p_bitmaps
->p_outline
= p_bitmaps
->p_glyph
;
1040 if( FT_Glyph_StrokeBorder( &p_bitmaps
->p_outline
,
1041 p_filter
->p_sys
->p_stroker
, 0, 0 ) )
1042 p_bitmaps
->p_outline
= 0;
1045 if( p_style
->i_shadow_alpha
!= STYLE_ALPHA_TRANSPARENT
)
1046 p_bitmaps
->p_shadow
= p_bitmaps
->p_outline
?
1047 p_bitmaps
->p_outline
: p_bitmaps
->p_glyph
;
1049 if( b_overwrite_advance
)
1051 p_bitmaps
->i_x_advance
= p_face
->glyph
->advance
.x
;
1052 p_bitmaps
->i_y_advance
= p_face
->glyph
->advance
.y
;
1056 int i_max_run_advance_x
= FT_FLOOR( FT_MulFix( p_face
->max_advance_width
, p_face
->size
->metrics
.x_scale
) );
1057 if( i_max_run_advance_x
> *pi_max_advance_x
)
1058 *pi_max_advance_x
= i_max_run_advance_x
;
1063 static int LayoutLine( filter_t
*p_filter
,
1064 paragraph_t
*p_paragraph
,
1065 int i_first_char
, int i_last_char
,
1066 line_desc_t
**pp_line
, bool b_grid
)
1068 if( p_paragraph
->i_size
<= 0 || p_paragraph
->i_runs_count
<= 0
1069 || i_first_char
< 0 || i_last_char
< 0
1070 || i_first_char
> i_last_char
1071 || i_last_char
>= p_paragraph
->i_size
)
1074 "LayoutLine() invalid parameters. "
1075 "Paragraph size: %d. Runs count: %d. "
1076 "Start char: %d. End char: %d",
1077 p_paragraph
->i_size
, p_paragraph
->i_runs_count
,
1078 i_first_char
, i_last_char
);
1079 return VLC_EGENERIC
;
1082 line_desc_t
*p_line
= NewLine( 1 + i_last_char
- i_first_char
);
1087 filter_sys_t
*p_sys
= p_filter
->p_sys
;
1088 int i_last_run
= -1;
1089 run_desc_t
*p_run
= 0;
1090 const text_style_t
*p_style
= 0;
1092 FT_Vector pen
= { .x
= 0, .y
= 0 };
1093 int i_line_index
= 0;
1095 int i_font_size
= 0;
1096 int i_font_width
= 0;
1097 int i_font_max_advance_y
= 0;
1098 int i_ul_offset
= 0;
1099 int i_ul_thickness
= 0;
1102 fribidi_reorder_line( 0, &p_paragraph
->p_types
[i_first_char
],
1103 1 + i_last_char
- i_first_char
,
1104 0, p_paragraph
->paragraph_type
,
1105 &p_paragraph
->p_levels
[i_first_char
],
1106 0, &p_paragraph
->pi_reordered_indices
[i_first_char
] );
1109 for( int i
= i_first_char
; i
<= i_last_char
; ++i
, ++i_line_index
)
1111 int i_paragraph_index
;
1113 i_paragraph_index
= p_paragraph
->pi_reordered_indices
[ i
];
1115 i_paragraph_index
= i
;
1118 line_character_t
*p_ch
= p_line
->p_character
+ i_line_index
;
1119 p_ch
->p_style
= p_paragraph
->pp_styles
[ i_paragraph_index
];
1121 glyph_bitmaps_t
*p_bitmaps
=
1122 p_paragraph
->p_glyph_bitmaps
+ i_paragraph_index
;
1124 if( !p_bitmaps
->p_glyph
)
1130 if( i_last_run
!= p_paragraph
->pi_run_ids
[ i_paragraph_index
] )
1132 i_last_run
= p_paragraph
->pi_run_ids
[ i_paragraph_index
];
1133 p_run
= p_paragraph
->p_runs
+ i_last_run
;
1134 p_style
= p_run
->p_style
;
1135 p_face
= p_run
->p_face
;
1137 i_font_width
= i_font_size
= ConvertToLiveSize( p_filter
, p_style
);
1138 if( p_style
->i_style_flags
& STYLE_HALFWIDTH
)
1140 else if( p_style
->i_style_flags
& STYLE_DOUBLEWIDTH
)
1144 FT_Vector pen_new
= {
1145 .x
= pen
.x
+ p_paragraph
->p_glyph_bitmaps
[ i_paragraph_index
].i_x_offset
,
1146 .y
= pen
.y
+ p_paragraph
->p_glyph_bitmaps
[ i_paragraph_index
].i_y_offset
1148 FT_Vector pen_shadow
= {
1149 .x
= pen_new
.x
+ p_sys
->f_shadow_vector_x
* ( i_font_width
<< 6 ),
1150 .y
= pen_new
.y
+ p_sys
->f_shadow_vector_y
* ( i_font_size
<< 6 )
1153 if( p_bitmaps
->p_shadow
)
1155 if( FT_Glyph_To_Bitmap( &p_bitmaps
->p_shadow
, FT_RENDER_MODE_NORMAL
,
1157 p_bitmaps
->p_shadow
= 0;
1159 FT_Glyph_Get_CBox( p_bitmaps
->p_shadow
, ft_glyph_bbox_pixels
,
1160 &p_bitmaps
->shadow_bbox
);
1162 if( p_bitmaps
->p_glyph
)
1164 if( FT_Glyph_To_Bitmap( &p_bitmaps
->p_glyph
, FT_RENDER_MODE_NORMAL
,
1167 FT_Done_Glyph( p_bitmaps
->p_glyph
);
1168 if( p_bitmaps
->p_outline
)
1169 FT_Done_Glyph( p_bitmaps
->p_outline
);
1170 if( p_bitmaps
->p_shadow
)
1171 FT_Done_Glyph( p_bitmaps
->p_shadow
);
1176 FT_Glyph_Get_CBox( p_bitmaps
->p_glyph
, ft_glyph_bbox_pixels
,
1177 &p_bitmaps
->glyph_bbox
);
1179 if( p_bitmaps
->p_outline
)
1181 if( FT_Glyph_To_Bitmap( &p_bitmaps
->p_outline
, FT_RENDER_MODE_NORMAL
,
1184 FT_Done_Glyph( p_bitmaps
->p_outline
);
1185 p_bitmaps
->p_outline
= 0;
1188 FT_Glyph_Get_CBox( p_bitmaps
->p_outline
, ft_glyph_bbox_pixels
,
1189 &p_bitmaps
->outline_bbox
);
1192 FixGlyph( p_bitmaps
->p_glyph
, &p_bitmaps
->glyph_bbox
,
1193 p_bitmaps
->i_x_advance
, p_bitmaps
->i_y_advance
,
1195 if( p_bitmaps
->p_outline
)
1196 FixGlyph( p_bitmaps
->p_outline
, &p_bitmaps
->outline_bbox
,
1197 p_bitmaps
->i_x_advance
, p_bitmaps
->i_y_advance
,
1199 if( p_bitmaps
->p_shadow
)
1200 FixGlyph( p_bitmaps
->p_shadow
, &p_bitmaps
->shadow_bbox
,
1201 p_bitmaps
->i_x_advance
, p_bitmaps
->i_y_advance
,
1204 int i_line_offset
= 0;
1205 int i_line_thickness
= 0;
1207 if( p_ch
->p_style
->i_style_flags
& (STYLE_UNDERLINE
| STYLE_STRIKEOUT
) )
1210 abs( FT_FLOOR( FT_MulFix( p_face
->underline_position
,
1211 p_face
->size
->metrics
.y_scale
) ) );
1214 abs( FT_CEIL( FT_MulFix( p_face
->underline_thickness
,
1215 p_face
->size
->metrics
.y_scale
) ) );
1217 if( p_ch
->p_style
->i_style_flags
& STYLE_STRIKEOUT
)
1219 /* Move the baseline to make it strikethrough instead of
1220 * underline. That means that strikethrough takes precedence
1223 abs( FT_FLOOR( FT_MulFix( p_face
->descender
* 2,
1224 p_face
->size
->metrics
.y_scale
) ) );
1225 p_bitmaps
->glyph_bbox
.yMax
=
1226 __MAX( p_bitmaps
->glyph_bbox
.yMax
,
1228 p_bitmaps
->glyph_bbox
.yMin
=
1229 __MIN( p_bitmaps
->glyph_bbox
.yMin
,
1230 i_line_offset
- i_line_thickness
);
1232 else if( i_line_thickness
> 0 )
1234 p_bitmaps
->glyph_bbox
.yMin
=
1235 __MIN( p_bitmaps
->glyph_bbox
.yMin
,
1236 - i_line_offset
- i_line_thickness
);
1238 /* The real underline thickness and position are
1239 * updated once the whole line has been parsed */
1240 i_ul_offset
= __MAX( i_ul_offset
, i_line_offset
);
1241 i_ul_thickness
= __MAX( i_ul_thickness
, i_line_thickness
);
1242 i_line_thickness
= -1;
1246 p_ch
->p_glyph
= ( FT_BitmapGlyph
) p_bitmaps
->p_glyph
;
1247 p_ch
->p_outline
= ( FT_BitmapGlyph
) p_bitmaps
->p_outline
;
1248 p_ch
->p_shadow
= ( FT_BitmapGlyph
) p_bitmaps
->p_shadow
;
1249 p_ch
->b_in_karaoke
= (p_paragraph
->pi_karaoke_bar
[ i_paragraph_index
] != 0);
1251 p_ch
->i_line_thickness
= i_line_thickness
;
1252 p_ch
->i_line_offset
= i_line_offset
;
1254 BBoxEnlarge( &p_line
->bbox
, &p_bitmaps
->glyph_bbox
);
1255 if( p_bitmaps
->p_outline
)
1256 BBoxEnlarge( &p_line
->bbox
, &p_bitmaps
->outline_bbox
);
1257 if( p_bitmaps
->p_shadow
)
1258 BBoxEnlarge( &p_line
->bbox
, &p_bitmaps
->shadow_bbox
);
1260 pen
.x
+= p_bitmaps
->i_x_advance
;
1261 pen
.y
+= p_bitmaps
->i_y_advance
;
1263 /* Get max advance for grid mode */
1264 if( b_grid
&& i_font_max_advance_y
== 0 && p_face
)
1266 i_font_max_advance_y
= abs( FT_FLOOR( FT_MulFix( p_face
->max_advance_height
,
1267 p_face
->size
->metrics
.y_scale
) ) );
1270 /* Keep track of blank/spaces in front/end of line */
1271 if( p_ch
->p_glyph
->bitmap
.rows
)
1273 if( p_line
->i_first_visible_char_index
< 0 )
1274 p_line
->i_first_visible_char_index
= i_line_index
;
1275 p_line
->i_last_visible_char_index
= i_line_index
;
1279 p_line
->i_width
= __MAX( 0, p_line
->bbox
.xMax
- p_line
->bbox
.xMin
);
1282 p_line
->i_height
= i_font_max_advance_y
;
1284 p_line
->i_height
= __MAX( 0, p_line
->bbox
.yMax
- p_line
->bbox
.yMin
);
1286 p_line
->i_character_count
= i_line_index
;
1288 if( i_ul_thickness
> 0 )
1290 for( int i
= 0; i
< p_line
->i_character_count
; i
++ )
1292 line_character_t
*ch
= &p_line
->p_character
[i
];
1293 if( ch
->i_line_thickness
< 0 )
1295 ch
->i_line_offset
= i_ul_offset
;
1296 ch
->i_line_thickness
= i_ul_thickness
;
1305 static inline void ReleaseGlyphBitMaps(glyph_bitmaps_t
*p_bitmaps
)
1307 if( p_bitmaps
->p_glyph
)
1308 FT_Done_Glyph( p_bitmaps
->p_glyph
);
1309 if( p_bitmaps
->p_outline
)
1310 FT_Done_Glyph( p_bitmaps
->p_outline
);
1313 static inline bool IsWhitespaceAt( paragraph_t
*p_paragraph
, size_t i
)
1315 return ( p_paragraph
->p_code_points
[ i
] == ' '
1317 || p_paragraph
->p_types
[ i
] == FRIBIDI_TYPE_WS
1322 static int LayoutParagraph( filter_t
*p_filter
, paragraph_t
*p_paragraph
,
1323 unsigned i_max_width
, unsigned i_max_advance_x
,
1324 line_desc_t
**pp_lines
, bool b_grid
, bool b_balance
)
1326 if( p_paragraph
->i_size
<= 0 || p_paragraph
->i_runs_count
<= 0 )
1328 msg_Err( p_filter
, "LayoutParagraph() invalid parameters. "
1329 "Paragraph size: %d. Runs count %d",
1330 p_paragraph
->i_size
, p_paragraph
->i_runs_count
);
1331 return VLC_EGENERIC
;
1335 * Check max line width to allow for outline and shadow glyphs,
1336 * and any extra width caused by visual reordering
1338 if( i_max_width
<= i_max_advance_x
)
1340 msg_Err( p_filter
, "LayoutParagraph(): Invalid max width" );
1341 return VLC_EGENERIC
;
1345 i_max_advance_x
<<= 6;
1347 int i_line_start
= 0;
1349 FT_Pos i_preferred_width
= i_max_width
;
1350 FT_Pos i_total_width
= 0;
1351 FT_Pos i_last_space_width
= 0;
1352 int i_last_space
= -1;
1353 line_desc_t
*p_first_line
= NULL
;
1354 line_desc_t
**pp_line
= &p_first_line
;
1356 for( int i
= 0; i
< p_paragraph
->i_size
; ++i
)
1359 p_paragraph
->pi_reordered_indices
[ i
] = i
;
1361 if( !IsWhitespaceAt( p_paragraph
, i
) || i
!= i_last_space
+ 1 )
1362 i_total_width
+= p_paragraph
->p_glyph_bitmaps
[ i
].i_x_advance
;
1368 if( i_total_width
== 0 )
1370 for( int i
=0; i
< p_paragraph
->i_size
; ++i
)
1371 ReleaseGlyphBitMaps( &p_paragraph
->p_glyph_bitmaps
[ i
] );
1377 int i_line_count
= i_total_width
/ (i_max_width
- i_max_advance_x
) + 1;
1378 i_preferred_width
= i_total_width
/ i_line_count
;
1381 for( int i
= 0; i
<= p_paragraph
->i_size
; ++i
)
1383 if( i
== p_paragraph
->i_size
)
1385 if( i_line_start
< i
)
1386 if( LayoutLine( p_filter
, p_paragraph
,
1387 i_line_start
, i
- 1, pp_line
, b_grid
) )
1393 if( IsWhitespaceAt( p_paragraph
, i
) )
1395 if( i_line_start
== i
)
1398 * Free orphaned white space glyphs not belonging to any lines.
1399 * At this point p_shadow points to either p_glyph or p_outline,
1400 * so we should not free it explicitly.
1402 ReleaseGlyphBitMaps( &p_paragraph
->p_glyph_bitmaps
[ i
] );
1403 i_line_start
= i
+ 1;
1407 if( i_last_space
== i
- 1 )
1409 p_paragraph
->p_glyph_bitmaps
[ i
- 1 ].i_x_advance
= 0;
1415 i_last_space_width
= i_width
;
1418 const run_desc_t
*p_run
= &p_paragraph
->p_runs
[p_paragraph
->pi_run_ids
[i
]];
1419 const int i_advance_x
= p_paragraph
->p_glyph_bitmaps
[ i
].i_x_advance
;
1421 if( ( i_last_space_width
+ i_advance_x
> i_preferred_width
&&
1422 p_run
->p_style
->e_wrapinfo
== STYLE_WRAP_DEFAULT
)
1423 || i_width
+ i_advance_x
> i_max_width
)
1425 if( i_line_start
== i
)
1427 /* If wrapping, algorithm would not end shifting lines down.
1428 * Not wrapping, that can't be rendered anymore. */
1429 msg_Dbg( p_filter
, "LayoutParagraph(): First glyph width in line exceeds maximum, skipping" );
1430 for( ; i
< p_paragraph
->i_size
; ++i
)
1431 ReleaseGlyphBitMaps( &p_paragraph
->p_glyph_bitmaps
[ i
] );
1435 int i_newline_start
;
1436 if( i_last_space
> i_line_start
&& p_run
->p_style
->e_wrapinfo
== STYLE_WRAP_DEFAULT
)
1437 i_newline_start
= i_last_space
; /* we break line on last space */
1439 i_newline_start
= i
; /* we break line on last char */
1441 if( LayoutLine( p_filter
, p_paragraph
, i_line_start
,
1442 i_newline_start
- 1, pp_line
, b_grid
) )
1445 /* Handle early end of renderable content;
1446 We're over size and we can't break space */
1447 if( p_run
->p_style
->e_wrapinfo
== STYLE_WRAP_NONE
)
1449 for( ; i
< p_paragraph
->i_size
; ++i
)
1450 ReleaseGlyphBitMaps( &p_paragraph
->p_glyph_bitmaps
[ i
] );
1454 pp_line
= &( *pp_line
)->p_next
;
1456 /* If we created a line up to previous space, we only keep the difference for
1457 our current width since that split */
1458 if( i_newline_start
== i_last_space
)
1460 i_width
= i_width
- i_last_space_width
;
1461 if( i_newline_start
+ 1 < p_paragraph
->i_size
)
1462 i_line_start
= i_newline_start
+ 1;
1464 i_line_start
= i_newline_start
; // == i
1469 i_line_start
= i_newline_start
;
1471 i_last_space_width
= 0;
1473 i_width
+= i_advance_x
;
1476 *pp_lines
= p_first_line
;
1480 for( int i
= i_line_start
; i
< p_paragraph
->i_size
; ++i
)
1481 ReleaseGlyphBitMaps( &p_paragraph
->p_glyph_bitmaps
[ i
] );
1483 FreeLines( p_first_line
);
1484 return VLC_EGENERIC
;
1487 int LayoutText( filter_t
*p_filter
,
1488 const uni_char_t
*psz_text
, text_style_t
**pp_styles
,
1489 uint32_t *pi_k_dates
, int i_len
,
1490 bool b_grid
, bool b_balance
,
1491 unsigned i_max_width
, unsigned i_max_height
,
1492 line_desc_t
**pp_lines
, FT_BBox
*p_bbox
, int *pi_max_face_height
)
1494 line_desc_t
*p_first_line
= 0;
1495 line_desc_t
**pp_line
= &p_first_line
;
1496 paragraph_t
*p_paragraph
= 0;
1497 int i_paragraph_start
= 0;
1498 unsigned i_total_height
= 0;
1499 unsigned i_max_advance_x
= 0;
1500 int i_max_face_height
= 0;
1502 for( int i
= 0; i
<= i_len
; ++i
)
1504 if( i
== i_len
|| psz_text
[ i
] == '\n' )
1506 if( i_paragraph_start
== i
)
1508 i_paragraph_start
= i
+ 1;
1512 p_paragraph
= NewParagraph( p_filter
, i
- i_paragraph_start
,
1513 psz_text
+ i_paragraph_start
,
1514 pp_styles
+ i_paragraph_start
,
1516 pi_k_dates
+ i_paragraph_start
: 0,
1520 if( p_first_line
) FreeLines( p_first_line
);
1525 if( AnalyzeParagraph( p_paragraph
) )
1529 if( ItemizeParagraph( p_filter
, p_paragraph
) )
1532 #if defined HAVE_HARFBUZZ
1533 if( ShapeParagraphHarfBuzz( p_filter
, &p_paragraph
) )
1536 if( LoadGlyphs( p_filter
, p_paragraph
, true, false, &i_max_advance_x
) )
1539 #elif defined HAVE_FRIBIDI
1540 if( ShapeParagraphFriBidi( p_filter
, p_paragraph
) )
1542 if( LoadGlyphs( p_filter
, p_paragraph
, false, true, &i_max_advance_x
) )
1544 if( RemoveZeroWidthCharacters( p_paragraph
) )
1546 if( ZeroNsmAdvance( p_paragraph
) )
1549 if( LoadGlyphs( p_filter
, p_paragraph
, false, true, &i_max_advance_x
) )
1553 if( LayoutParagraph( p_filter
, p_paragraph
,
1554 i_max_width
, i_max_advance_x
, pp_line
,
1555 b_grid
, b_balance
) )
1558 FreeParagraph( p_paragraph
);
1561 for( ; *pp_line
; pp_line
= &(*pp_line
)->p_next
)
1563 i_total_height
+= (*pp_line
)->i_height
;
1564 if( i_max_height
> 0 && i_total_height
> i_max_height
)
1566 i_total_height
= i_max_height
+ 1;
1567 line_desc_t
*p_todelete
= *pp_line
;
1568 while( p_todelete
) /* Drop extra lines */
1570 line_desc_t
*p_next
= p_todelete
->p_next
;
1571 FreeLine( p_todelete
);
1572 p_todelete
= p_next
;
1575 i
= i_len
+ 1; /* force no more paragraphs */
1576 break; /* ! no p_next ! */
1578 else if( (*pp_line
)->i_height
> i_max_face_height
)
1580 i_max_face_height
= (*pp_line
)->i_height
;
1583 i_paragraph_start
= i
+ 1;
1587 int i_base_line
= 0;
1595 for( line_desc_t
*p_line
= p_first_line
; p_line
; p_line
= p_line
->p_next
)
1597 p_line
->i_base_line
= i_base_line
;
1598 p_line
->bbox
.yMin
-= i_base_line
;
1599 p_line
->bbox
.yMax
-= i_base_line
;
1600 BBoxEnlarge( &bbox
, &p_line
->bbox
);
1602 i_base_line
+= i_max_face_height
;
1605 *pi_max_face_height
= i_max_face_height
;
1606 *pp_lines
= p_first_line
;
1611 if( p_first_line
) FreeLines( p_first_line
);
1612 if( p_paragraph
) FreeParagraph( p_paragraph
);
1613 return VLC_EGENERIC
;