Exemplo n.º 1
0
void CHTMLSectionCreator::AddDocument( CHTMLDocument *pDocument )
{
	m_cCharSet = pDocument->m_cCharSet;

	m_nLineWidth = GetCurrentWidth();

	//	Pointers used to process the anchor information
	CHTMLAnchor* pLastAnchor = NULL;
	const UINT uDocumentSize = pDocument->m_arrItems.GetSize();
	for( UINT n = 0; n < uDocumentSize; n++ )
	{
		CHTMLDocumentObject *pItem = pDocument->m_arrItems[ n ];
		switch( pItem->GetType() )
		{
		case CHTMLDocumentObject::knParagraph:
			{
				CHTMLParagraph *pPara = static_cast<CHTMLParagraph *>( pItem );
				if( pPara->IsEmpty() )
					continue;

				pPara->AddDisplayElements( this );

				NewParagraph( pPara->m_nSpaceAbove, pPara->m_nSpaceBelow, pPara->m_alg );

				const UINT uParagraphSize = pPara->m_arrItems.GetSize();
				for( UINT nItem = 0; nItem < uParagraphSize; nItem++ )
				{
					CHTMLParagraphObject *pParaObj = pPara->m_arrItems[ nItem ];

					// Update the Anchor status
					if (pParaObj->m_pAnchor == NULL || pParaObj->m_pAnchor != pLastAnchor)
						m_pCurrentLink = NULL;
		

					switch( pParaObj->GetType() )
					{
					case CHTMLParagraphObject::knAnchor:
						{
							pParaObj->AddDisplayElements( this );	

							CHTMLAnchor *pAnchor = static_cast<CHTMLAnchor *>( pParaObj );
							pLastAnchor = pAnchor;
						}
						break;

					default:
						pParaObj->AddDisplayElements( this );	
					}
				}
			}
			break;
		}
	}
	Finished();
}
Exemplo n.º 2
0
static int LayoutLine( filter_t *p_filter,
                       paragraph_t *p_paragraph,
                       int i_start_offset, int i_end_offset,
                       line_desc_t **pp_line )
{
    if( p_paragraph->i_size <= 0 || p_paragraph->i_runs_count <= 0
     || i_start_offset >= i_end_offset
     || i_start_offset < 0 || i_start_offset >= p_paragraph->i_size
     || i_end_offset <= 0  || i_end_offset > p_paragraph->i_size )
    {
        msg_Err( p_filter,
                 "LayoutLine() invalid parameters. "
                 "Paragraph size: %d. Runs count: %d. "
                 "Start offset: %d. End offset: %d",
                 p_paragraph->i_size, p_paragraph->i_runs_count,
                 i_start_offset, i_end_offset );
        return VLC_EGENERIC;
    }

    line_desc_t *p_line = NewLine( i_end_offset - i_start_offset );

    if( !p_line )
        return VLC_ENOMEM;

    filter_sys_t *p_sys = p_filter->p_sys;
    int i_last_run = -1;
    run_desc_t *p_run = 0;
    text_style_t *p_style = 0;
    FT_Face p_face = 0;
    FT_Vector pen = { .x = 0, .y = 0 };
    int i_line_index = 0;

    int i_font_width = 0;
    int i_ul_offset = 0;
    int i_ul_thickness = 0;

#ifdef HAVE_FRIBIDI
    fribidi_reorder_line( 0, p_paragraph->p_types + i_start_offset,
                          i_end_offset - i_start_offset,
                          0, p_paragraph->paragraph_type,
                          p_paragraph->p_levels + i_start_offset,
                          0, p_paragraph->pi_reordered_indices + i_start_offset );
#endif

    for( int i = i_start_offset; i < i_end_offset; ++i, ++i_line_index )
    {
        int i_paragraph_index;
#ifdef HAVE_FRIBIDI
        i_paragraph_index = p_paragraph->pi_reordered_indices[ i ];
#else
        i_paragraph_index = i;
#endif

        line_character_t *p_ch = p_line->p_character + i_line_index;
        glyph_bitmaps_t *p_bitmaps =
                p_paragraph->p_glyph_bitmaps + i_paragraph_index;

        if( !p_bitmaps->p_glyph )
        {
            --i_line_index;
            continue;
        }

        if( i_last_run != p_paragraph->pi_run_ids[ i_paragraph_index ] )
        {
            i_last_run = p_paragraph->pi_run_ids[ i_paragraph_index ];
            p_run = p_paragraph->p_runs + i_last_run;
            p_style = p_run->p_style;
            p_face = p_run->p_face;

            i_font_width = p_style->i_style_flags & STYLE_HALFWIDTH ?
                           p_style->i_font_size / 2 : p_style->i_font_size;
        }

        FT_Vector pen_new = {
            .x = pen.x + p_paragraph->p_glyph_bitmaps[ i_paragraph_index ].i_x_offset,
            .y = pen.y + p_paragraph->p_glyph_bitmaps[ i_paragraph_index ].i_y_offset
        };
        FT_Vector pen_shadow = {
            .x = pen_new.x + p_sys->f_shadow_vector_x * ( i_font_width << 6 ),
            .y = pen_new.y + p_sys->f_shadow_vector_y * ( p_style->i_font_size << 6 )
        };

        if( p_bitmaps->p_shadow )
        {
            if( FT_Glyph_To_Bitmap( &p_bitmaps->p_shadow, FT_RENDER_MODE_NORMAL,
                                    &pen_shadow, 0 ) )
                p_bitmaps->p_shadow = 0;
            else
                FT_Glyph_Get_CBox( p_bitmaps->p_shadow, ft_glyph_bbox_pixels,
                                   &p_bitmaps->shadow_bbox );
        }
        if( p_bitmaps->p_glyph )
        {
            if( FT_Glyph_To_Bitmap( &p_bitmaps->p_glyph, FT_RENDER_MODE_NORMAL,
                                    &pen_new, 1 ) )
            {
                FT_Done_Glyph( p_bitmaps->p_glyph );
                if( p_bitmaps->p_outline )
                    FT_Done_Glyph( p_bitmaps->p_outline );
                if( p_bitmaps->p_shadow )
                    FT_Done_Glyph( p_bitmaps->p_shadow );
                --i_line_index;
                continue;
            }
            else
                FT_Glyph_Get_CBox( p_bitmaps->p_glyph, ft_glyph_bbox_pixels,
                                   &p_bitmaps->glyph_bbox );
        }
        if( p_bitmaps->p_outline )
        {
            if( FT_Glyph_To_Bitmap( &p_bitmaps->p_outline, FT_RENDER_MODE_NORMAL,
                                    &pen_new, 1 ) )
            {
                FT_Done_Glyph( p_bitmaps->p_outline );
                p_bitmaps->p_outline = 0;
            }
            else
                FT_Glyph_Get_CBox( p_bitmaps->p_outline, ft_glyph_bbox_pixels,
                                   &p_bitmaps->outline_bbox );
        }

        FixGlyph( p_bitmaps->p_glyph, &p_bitmaps->glyph_bbox,
                  p_bitmaps->i_x_advance, p_bitmaps->i_y_advance,
                  &pen_new );
        if( p_bitmaps->p_outline )
            FixGlyph( p_bitmaps->p_outline, &p_bitmaps->outline_bbox,
                      p_bitmaps->i_x_advance, p_bitmaps->i_y_advance,
                      &pen_new );
        if( p_bitmaps->p_shadow )
            FixGlyph( p_bitmaps->p_shadow, &p_bitmaps->shadow_bbox,
                      p_bitmaps->i_x_advance, p_bitmaps->i_y_advance,
                      &pen_shadow );

        int i_line_offset    = 0;
        int i_line_thickness = 0;
        text_style_t *p_glyph_style = p_paragraph->pp_styles[ i_paragraph_index ];
        if( p_glyph_style->i_style_flags & (STYLE_UNDERLINE | STYLE_STRIKEOUT) )
        {
            i_line_offset =
                abs( FT_FLOOR( FT_MulFix( p_face->underline_position,
                                          p_face->size->metrics.y_scale ) ) );

            i_line_thickness =
                abs( FT_CEIL( FT_MulFix( p_face->underline_thickness,
                                         p_face->size->metrics.y_scale ) ) );

            if( p_glyph_style->i_style_flags & STYLE_STRIKEOUT )
            {
                /* Move the baseline to make it strikethrough instead of
                 * underline. That means that strikethrough takes precedence
                 */
                i_line_offset -=
                    abs( FT_FLOOR( FT_MulFix( p_face->descender * 2,
                                              p_face->size->metrics.y_scale ) ) );
            }
            else if( i_line_thickness > 0 )
            {
                p_bitmaps->glyph_bbox.yMin =
                    __MIN( p_bitmaps->glyph_bbox.yMin,
                           - i_line_offset - i_line_thickness );

                /* The real underline thickness and position are
                 * updated once the whole line has been parsed */
                i_ul_offset = __MAX( i_ul_offset, i_line_offset );
                i_ul_thickness = __MAX( i_ul_thickness, i_line_thickness );
                i_line_thickness = -1;
            }
        }

        p_ch->p_glyph = ( FT_BitmapGlyph ) p_bitmaps->p_glyph;
        p_ch->p_outline = ( FT_BitmapGlyph ) p_bitmaps->p_outline;
        p_ch->p_shadow = ( FT_BitmapGlyph ) p_bitmaps->p_shadow;

        bool b_karaoke = p_paragraph->pi_karaoke_bar[ i_paragraph_index ] != 0;
        p_ch->i_color = b_karaoke ?
                        ( uint32_t ) p_glyph_style->i_karaoke_background_color
                      |              p_glyph_style->i_karaoke_background_alpha << 24
                      : ( uint32_t ) p_glyph_style->i_font_color
                      |              p_glyph_style->i_font_alpha << 24;

        p_ch->i_line_thickness = i_line_thickness;
        p_ch->i_line_offset = i_line_offset;

        BBoxEnlarge( &p_line->bbox, &p_bitmaps->glyph_bbox );
        if( p_bitmaps->p_outline )
            BBoxEnlarge( &p_line->bbox, &p_bitmaps->outline_bbox );
        if( p_bitmaps->p_shadow )
            BBoxEnlarge( &p_line->bbox, &p_bitmaps->shadow_bbox );

        pen.x += p_bitmaps->i_x_advance;
        pen.y += p_bitmaps->i_y_advance;
    }

    p_line->i_width = __MAX( 0, p_line->bbox.xMax - p_line->bbox.xMin );
    p_line->i_height = __MAX( 0, p_line->bbox.yMax - p_line->bbox.yMin );
    p_line->i_character_count = i_line_index;

    if( i_ul_thickness > 0 )
    {
        for( int i = 0; i < p_line->i_character_count; i++ )
        {
            line_character_t *ch = &p_line->p_character[i];
            if( ch->i_line_thickness < 0 )
            {
                ch->i_line_offset    = i_ul_offset;
                ch->i_line_thickness = i_ul_thickness;
            }
        }
    }

    *pp_line = p_line;
    return VLC_SUCCESS;
}

static int LayoutParagraph( filter_t *p_filter, paragraph_t *p_paragraph,
                            int i_max_pixel_width, line_desc_t **pp_lines )
{
    if( p_paragraph->i_size <= 0 || p_paragraph->i_runs_count <= 0 )
    {
        msg_Err( p_filter, "LayoutParagraph() invalid parameters. "
                 "Paragraph size: %d. Runs count %d",
                 p_paragraph->i_size, p_paragraph->i_runs_count );
        return VLC_EGENERIC;
    }

    int i_line_start = 0;
    FT_Pos i_width = 0;
    FT_Pos i_max_width = i_max_pixel_width << 6;
    FT_Pos i_preferred_width = 0;
    FT_Pos i_total_width = 0;
    FT_Pos i_last_space_width = 0;
    int i_last_space = -1;
    line_desc_t *p_first_line = 0;
    line_desc_t **pp_line = &p_first_line;

    for( int i = 0; i < p_paragraph->i_size; ++i )
    {
#ifdef HAVE_FRIBIDI
        p_paragraph->pi_reordered_indices[ i ] = i;
#endif
        i_total_width += p_paragraph->p_glyph_bitmaps[ i ].i_x_advance;
    }

    int i_line_count = i_total_width / i_max_width + 1;
    i_preferred_width = i_total_width / i_line_count;

    for( int i = 0; i <= p_paragraph->i_size; ++i )
    {
        if( i == p_paragraph->i_size )
        {
            if( i_line_start < i )
                if( LayoutLine( p_filter, p_paragraph,
                                i_line_start, i, pp_line ) )
                    goto error;

            break;
        }

        if( p_paragraph->p_code_points[ i ] == ' '
#ifdef HAVE_FRIBIDI
            || p_paragraph->p_types[ i ] == FRIBIDI_TYPE_WS
#endif
          )
        {
            if( i_line_start == i )
            {
                /*
                 * Free orphaned white space glyphs not belonging to any lines.
                 * At this point p_shadow points to either p_glyph or p_outline,
                 * so we should not free it explicitly.
                 */
                if( p_paragraph->p_glyph_bitmaps[ i ].p_glyph )
                    FT_Done_Glyph( p_paragraph->p_glyph_bitmaps[ i ].p_glyph );
                if( p_paragraph->p_glyph_bitmaps[ i ].p_outline )
                    FT_Done_Glyph( p_paragraph->p_glyph_bitmaps[ i ].p_outline );

                i_line_start = i + 1;
                continue;
            }

            if( i_last_space == i - 1 )
            {
                p_paragraph->p_glyph_bitmaps[ i - 1 ].i_x_advance = 0;
                i_last_space = i;
                continue;
            }

            i_last_space = i;
            i_last_space_width = i_width;
        }

        i_width += p_paragraph->p_glyph_bitmaps[ i ].i_x_advance;

        if( i_last_space_width >= i_preferred_width
         || i_width >= i_max_width )
        {
            if( i_line_start == i )
            {
                msg_Err( p_filter,
                         "LayoutParagraph(): Width of single glyph exceeds maximum" );
                goto error;
            }

            int i_end_offset;
            if( i_last_space > i_line_start )
                i_end_offset = i_last_space;
            else
                i_end_offset = i;

            if( LayoutLine( p_filter, p_paragraph, i_line_start,
                            i_end_offset, pp_line ) )
                goto error;

            pp_line = &( *pp_line )->p_next;
            i_line_start = i_end_offset;
            i = i_line_start - 1;
            i_width = 0;
            i_last_space_width = 0;
        }
    }

    *pp_lines = p_first_line;
    return VLC_SUCCESS;

error:
    for( int i = i_line_start; i < p_paragraph->i_size; ++i )
    {
        if( p_paragraph->p_glyph_bitmaps[ i ].p_glyph )
            FT_Done_Glyph( p_paragraph->p_glyph_bitmaps[ i ].p_glyph );
        if( p_paragraph->p_glyph_bitmaps[ i ].p_outline )
            FT_Done_Glyph( p_paragraph->p_glyph_bitmaps[ i ].p_outline );
    }
    if( p_first_line )
        FreeLines( p_first_line );
    return VLC_EGENERIC;
}

int LayoutText( filter_t *p_filter, line_desc_t **pp_lines,
                FT_BBox *p_bbox, int *pi_max_face_height,

                uni_char_t *psz_text, text_style_t **pp_styles,
                uint32_t *pi_k_dates, int i_len )
{
    line_desc_t *p_first_line = 0;
    line_desc_t **pp_line = &p_first_line;
    paragraph_t *p_paragraph = 0;
    int i_paragraph_start = 0;
    int i_max_height = 0;

    for( int i = 0; i <= i_len; ++i )
    {
        if( i == i_len || psz_text[ i ] == '\n' )
        {
            if( i_paragraph_start == i )
            {
                i_paragraph_start = i + 1;
                continue;
            }

            p_paragraph = NewParagraph( p_filter, i - i_paragraph_start,
                                        psz_text + i_paragraph_start,
                                        pp_styles + i_paragraph_start,
                                        pi_k_dates ?
                                        pi_k_dates + i_paragraph_start : 0,
                                        20 );
            if( !p_paragraph )
            {
                if( p_first_line ) FreeLines( p_first_line );
                return VLC_ENOMEM;
            }

#ifdef HAVE_FRIBIDI
            if( AnalyzeParagraph( p_paragraph ) )
                goto error;
#endif

            if( ItemizeParagraph( p_filter, p_paragraph ) )
                goto error;

#if defined HAVE_HARFBUZZ
            if( ShapeParagraphHarfBuzz( p_filter, &p_paragraph ) )
                goto error;

            if( LoadGlyphs( p_filter, p_paragraph, true, false ) )
                goto error;

#elif defined HAVE_FRIBIDI
            if( ShapeParagraphFriBidi( p_filter, p_paragraph ) )
                goto error;
            if( LoadGlyphs( p_filter, p_paragraph, false, true ) )
                goto error;
            if( RemoveZeroWidthCharacters( p_paragraph ) )
                goto error;
            if( ZeroNsmAdvance( p_paragraph ) )
                goto error;
#else
            if( LoadGlyphs( p_filter, p_paragraph, false, true ) )
                goto error;
#endif

            /*
             * Set max line width to allow for outline and shadow glyphs,
             * and any extra width caused by visual reordering
             */
            int i_max_width = ( int ) p_filter->fmt_out.video.i_visible_width
                              - 2 * p_filter->p_sys->style.i_font_size;
            if( LayoutParagraph( p_filter, p_paragraph,
                                 i_max_width, pp_line ) )
                goto error;

            FreeParagraph( p_paragraph );
            p_paragraph = 0;

            for( ; *pp_line; pp_line = &( *pp_line )->p_next )
                i_max_height = __MAX( i_max_height, ( *pp_line )->i_height );

            i_paragraph_start = i + 1;
        }
    }

    int i_base_line = 0;
    FT_BBox bbox = {
        .xMin = INT_MAX,
        .yMin = INT_MAX,
        .xMax = INT_MIN,
        .yMax = INT_MIN
    };

    for( line_desc_t *p_line = p_first_line; p_line; p_line = p_line->p_next )
    {
        p_line->i_base_line = i_base_line;
        p_line->bbox.yMin -= i_base_line;
        p_line->bbox.yMax -= i_base_line;
        BBoxEnlarge( &bbox, &p_line->bbox );

        i_base_line += i_max_height;
    }

    *pp_lines = p_first_line;
    *p_bbox = bbox;
    *pi_max_face_height = i_max_height;
    return VLC_SUCCESS;

error:
    if( p_first_line ) FreeLines( p_first_line );
    if( p_paragraph ) FreeParagraph( p_paragraph );
    return VLC_EGENERIC;
}
Exemplo n.º 3
0
static int ShapeParagraphHarfBuzz( filter_t *p_filter,
                                   paragraph_t **p_old_paragraph )
{
    paragraph_t *p_paragraph = *p_old_paragraph;
    paragraph_t *p_new_paragraph = 0;
    filter_sys_t *p_sys = p_filter->p_sys;
    int i_total_glyphs = 0;
    int i_ret = VLC_EGENERIC;

    if( p_paragraph->i_size <= 0 || p_paragraph->i_runs_count <= 0 )
    {
        msg_Err( p_filter, "ShapeParagraphHarfBuzz() invalid parameters. "
                 "Paragraph size: %d. Runs count %d",
                 p_paragraph->i_size, p_paragraph->i_runs_count );
        return VLC_EGENERIC;
    }

    for( int i = 0; i < p_paragraph->i_runs_count; ++i )
    {
        run_desc_t *p_run = p_paragraph->p_runs + i;
        text_style_t *p_style = p_run->p_style;

        /*
         * When using HarfBuzz, this is where font faces are loaded.
         * In the other two paths (shaping with FriBidi or no
         * shaping at all), faces are loaded in LoadGlyphs()
         */
        FT_Face p_face = 0;
        if( !p_run->p_face )
        {
            p_face = LoadFace( p_filter, p_style );
            if( !p_face )
            {
                p_face = p_sys->p_face;
                p_style = &p_sys->style;
                p_run->p_style = p_style;
            }
            p_run->p_face = p_face;
        }
        else
            p_face = p_run->p_face;

        p_run->p_hb_font = hb_ft_font_create( p_face, 0 );
        if( !p_run->p_hb_font )
        {
            msg_Err( p_filter,
                     "ShapeParagraphHarfBuzz(): hb_ft_font_create() error" );
            goto error;
        }

        p_run->p_buffer = hb_buffer_create();
        if( !p_run->p_buffer )
        {
            msg_Err( p_filter,
                     "ShapeParagraphHarfBuzz(): hb_buffer_create() error" );
            goto error;
        }

        hb_buffer_set_direction( p_run->p_buffer, p_run->direction );
        hb_buffer_set_script( p_run->p_buffer, p_run->script );
#ifdef __OS2__
        hb_buffer_add_utf16( p_run->p_buffer,
                             p_paragraph->p_code_points + p_run->i_start_offset,
                             p_run->i_end_offset - p_run->i_start_offset, 0,
                             p_run->i_end_offset - p_run->i_start_offset );
#else
        hb_buffer_add_utf32( p_run->p_buffer,
                             p_paragraph->p_code_points + p_run->i_start_offset,
                             p_run->i_end_offset - p_run->i_start_offset, 0,
                             p_run->i_end_offset - p_run->i_start_offset );
#endif
        hb_shape( p_run->p_hb_font, p_run->p_buffer, 0, 0 );
        p_run->p_glyph_infos =
            hb_buffer_get_glyph_infos( p_run->p_buffer, &p_run->i_glyph_count );
        p_run->p_glyph_positions =
            hb_buffer_get_glyph_positions( p_run->p_buffer, &p_run->i_glyph_count );

        if( p_run->i_glyph_count <= 0 )
        {
            msg_Err( p_filter,
                     "ShapeParagraphHarfBuzz() invalid glyph count in shaped run" );
            goto error;
        }

        i_total_glyphs += p_run->i_glyph_count;
    }

    p_new_paragraph = NewParagraph( p_filter, i_total_glyphs, 0, 0, 0,
                                    p_paragraph->i_runs_size );
    if( !p_new_paragraph )
    {
        i_ret = VLC_ENOMEM;
        goto error;
    }
    p_new_paragraph->paragraph_type = p_paragraph->paragraph_type;

    int i_index = 0;
    for( int i = 0; i < p_paragraph->i_runs_count; ++i )
    {
        run_desc_t *p_run = p_paragraph->p_runs + i;
        hb_glyph_info_t *p_infos = p_run->p_glyph_infos;
        hb_glyph_position_t *p_positions = p_run->p_glyph_positions;
        for( unsigned int j = 0; j < p_run->i_glyph_count; ++j )
        {
            /*
             * HarfBuzz reverses the order of glyphs in RTL runs. We reverse
             * it again here to keep the glyphs in their logical order.
             * For line breaking of paragraphs to work correctly, visual
             * reordering should be done after line breaking has taken
             * place.
             */
            int i_run_index = p_run->direction == HB_DIRECTION_LTR ?
                    j : p_run->i_glyph_count - 1 - j;
            int i_source_index =
                    p_infos[ i_run_index ].cluster + p_run->i_start_offset;

            p_new_paragraph->p_code_points[ i_index ] = 0;
            p_new_paragraph->pi_glyph_indices[ i_index ] =
                p_infos[ i_run_index ].codepoint;
            p_new_paragraph->p_scripts[ i_index ] =
                p_paragraph->p_scripts[ i_source_index ];
            p_new_paragraph->p_types[ i_index ] =
                p_paragraph->p_types[ i_source_index ];
            p_new_paragraph->p_levels[ i_index ] =
                p_paragraph->p_levels[ i_source_index ];
            p_new_paragraph->pp_styles[ i_index ] =
                p_paragraph->pp_styles[ i_source_index ];
            p_new_paragraph->pi_karaoke_bar[ i_index ] =
                p_paragraph->pi_karaoke_bar[ i_source_index ];
            p_new_paragraph->p_glyph_bitmaps[ i_index ].i_x_offset =
                p_positions[ i_run_index ].x_offset;
            p_new_paragraph->p_glyph_bitmaps[ i_index ].i_y_offset =
                p_positions[ i_run_index ].y_offset;
            p_new_paragraph->p_glyph_bitmaps[ i_index ].i_x_advance =
                p_positions[ i_run_index ].x_advance;
            p_new_paragraph->p_glyph_bitmaps[ i_index ].i_y_advance =
                p_positions[ i_run_index ].y_advance;

            ++i_index;
        }
        if( AddRun( p_filter, p_new_paragraph, i_index - p_run->i_glyph_count,
                    i_index, p_run->p_face ) )
            goto error;
    }

    for( int i = 0; i < p_paragraph->i_runs_count; ++i )
    {
        hb_font_destroy( p_paragraph->p_runs[ i ].p_hb_font );
        hb_buffer_destroy( p_paragraph->p_runs[ i ].p_buffer );
    }
    FreeParagraph( *p_old_paragraph );
    *p_old_paragraph = p_new_paragraph;

    return VLC_SUCCESS;

error:
    for( int i = 0; i < p_paragraph->i_runs_count; ++i )
    {
        if( p_paragraph->p_runs[ i ].p_hb_font )
            hb_font_destroy( p_paragraph->p_runs[ i ].p_hb_font );
        if( p_paragraph->p_runs[ i ].p_buffer )
            hb_buffer_destroy( p_paragraph->p_runs[ i ].p_buffer );
    }

    if( p_new_paragraph )
        FreeParagraph( p_new_paragraph );

    return i_ret;
}