def split_first_line(text, style, context, max_width, justification_spacing,
                     is_line_start=True, minimum=False):
    """Fit as much as possible in the available width for one line of text.

    Return ``(layout, length, resume_index, width, height, baseline)``.

    ``layout``: a pango Layout with the first line
    ``length``: length in UTF-8 bytes of the first line
    ``resume_index``: The number of UTF-8 bytes to skip for the next line.
                      May be ``None`` if the whole text fits in one line.
                      This may be greater than ``length`` in case of preserved
                      newline characters.
    ``width``: width in pixels of the first line
    ``height``: height in pixels of the first line
    ``baseline``: baseline in pixels of the first line

    """
    # See https://www.w3.org/TR/css-text-3/#white-space-property
    text_wrap = style['white_space'] in ('normal', 'pre-wrap', 'pre-line')
    space_collapse = style['white_space'] in ('normal', 'nowrap', 'pre-line')

    original_max_width = max_width
    if not text_wrap:
        max_width = None

    # Step #1: Get a draft layout with the first line.
    ratio = 4  # number that almost always respects char_height / char_width > ratio
    short_text = text
    if max_width is not None and max_width != inf and style['font_size']:
        # Try to use a small amount of text to avoid the whole layout. We need
        # at least one line, and one possible line break point on the second line.
        if style['font_size'] * ratio > max_width:
            # Trying to find minimum or very small size, let's naively split on
            # spaces and keep one word + one letter.
            space_index = text.find(' ')
            if space_index != -1:
                short_text = text[:space_index+2]  # index + space + one letter
        else:
            # Use the magic ration and hope that we’ll get the right amount of text.
            short_text = text[:int(max_width / style['font_size'] * ratio)]
        layout = create_layout(
            short_text, style, context, max_width, justification_spacing)
        first_line, resume_index = layout.get_first_line()
        if resume_index is None and short_text != text:
            # The small amount of text fits in one line, give up and use the
            # whole text.
            short_text = text
            layout.set_text(text)
            first_line, resume_index = layout.get_first_line()
        else:
            # If the second line of the short text can break, we have the next
            # line break point required for step #3 in it, drop the end of the text.
            first_line_text = short_text.encode()[:resume_index].decode()
            if first_line_text != short_text:
                start, end = len(first_line_text) + 1, len(short_text)
                text_end_log_attrs = pango.pango_layout_get_log_attrs_readonly(
                    layout.layout, ffi.NULL)[start:end]
                if get_next_break_point(text_end_log_attrs) is not None:
                    text = short_text
    else:
        layout = create_layout(
            text, style, context, original_max_width, justification_spacing)
        first_line, resume_index = layout.get_first_line()

    # Step #2: Don't split lines when it's not needed.
    if max_width is None:
        # The first line can take all the place needed.
        return first_line_metrics(
            first_line, text, layout, resume_index, space_collapse, style)
    first_line_width, _ = line_size(first_line, style)
    if resume_index is None and first_line_width <= max_width:
        # The first line fits in the available width.
        return first_line_metrics(
            first_line, text, layout, resume_index, space_collapse, style)

    # Step #3: Try to put the first word of the second line on the first line
    # https://mail.gnome.org/archives/gtk-i18n-list/2013-September/msg00006
    # is a good thread related to this problem.
    if first_line_width <= max_width:
        # The first line fits but may have been cut too early by Pango.
        encoded_text = text.encode()
        first_line_text = encoded_text[:resume_index].decode()
        second_line_text = encoded_text[resume_index:].decode()
    else:
        # The line can't be split earlier, try to hyphenate the first word.
        first_line_text = ''
        second_line_text = text
    if first_line_text == short_text:
        # There’s no second line, don’t try to find a next word.
        break_point = None
    else:
        # Find then second line’s first break point.
        log_attrs = pango.pango_layout_get_log_attrs_readonly(layout.layout, ffi.NULL)
        start, end = len(first_line_text) + 1, len(short_text)
        second_line_log_attrs = log_attrs[start:end]
        break_point = get_next_break_point(second_line_log_attrs)
        if break_point is not None:
            break_point -= len(first_line_text) + 1
    next_word = second_line_text[:break_point].rstrip(' ')
    if next_word:
        if space_collapse and second_line_text[break_point or -1] == ' ':
            # Next word might fit without a space afterwards only try when
            # space collapsing is allowed.
            new_first_line_text = first_line_text + next_word
            layout.set_text(new_first_line_text)
            first_line, resume_index = layout.get_first_line()
            if resume_index is None:
                if first_line_text:
                    # The next word fits in the first line, keep the layout.
                    resume_index = len(new_first_line_text.encode()) + 1
                    return first_line_metrics(
                        first_line, text, layout, resume_index, space_collapse, style)
                else:
                    # Second line is None.
                    resume_index = first_line.length + 1
                    if resume_index >= len(text.encode()):
                        resume_index = None
    elif first_line_text:
        # We found something on the first line but we did not find a word on
        # the next line, no need to hyphenate, we can keep the current layout.
        return first_line_metrics(
            first_line, text, layout, resume_index, space_collapse, style)

    # Step #4: Try to hyphenate
    hyphens = style['hyphens']
    lang = style['lang'] and pyphen.language_fallback(style['lang'])
    total, left, right = style['hyphenate_limit_chars']
    hyphenated = False
    soft_hyphen = '\xad'

    auto_hyphenation = manual_hyphenation = False
    if hyphens != 'none':
        manual_hyphenation = soft_hyphen in first_line_text + second_line_text
    if hyphens == 'auto' and lang:
        next_word_boundaries = get_next_word_boundaries(second_line_text, lang)
        if next_word_boundaries:
            # We have a word to hyphenate
            start_word, stop_word = next_word_boundaries
            next_word = second_line_text[start_word:stop_word]
            if stop_word - start_word >= total:
                # This word is long enough
                first_line_width, _ = line_size(first_line, style)
                space = max_width - first_line_width
                if style['hyphenate_limit_zone'].unit == '%':
                    limit_zone = (
                        max_width * style['hyphenate_limit_zone'].value / 100)
                else:
                    limit_zone = style['hyphenate_limit_zone'].value
                if space > limit_zone or space < 0:
                    # Available space is worth the try, or the line is even too
                    # long to fit: try to hyphenate
                    auto_hyphenation = True

    # Automatic hyphenation opportunities within a word must be ignored if the
    # word contains a conditional hyphen, in favor of the conditional
    # hyphen(s).
    # See https://drafts.csswg.org/css-text-3/#valdef-hyphens-auto
    if manual_hyphenation:
        # Manual hyphenation: check that the line ends with a soft
        # hyphen and add the missing hyphen
        if first_line_text.endswith(soft_hyphen):
            # The first line has been split on a soft hyphen
            first_line_text, second_line_text = '', first_line_text
        soft_hyphen_indexes = [
            match.start() for match in re.finditer(soft_hyphen, second_line_text)]
        soft_hyphen_indexes.reverse()
        dictionary_iterations = [second_line_text[:i+1] for i in soft_hyphen_indexes]
        start_word = 0
    elif auto_hyphenation:
        dictionary_key = (lang, left, right, total)
        dictionary = context.dictionaries.get(dictionary_key)
        if dictionary is None:
            dictionary = pyphen.Pyphen(lang=lang, left=left, right=right)
            context.dictionaries[dictionary_key] = dictionary
        dictionary_iterations = [
            start for start, end in dictionary.iterate(next_word)]
    else:
        dictionary_iterations = []

    if dictionary_iterations:
        for first_word_part in dictionary_iterations:
            new_first_line_text = (
                first_line_text +
                second_line_text[:start_word] +
                first_word_part)
            hyphenated_first_line_text = (
                new_first_line_text + style['hyphenate_character'])
            new_layout = create_layout(
                hyphenated_first_line_text, style, context, max_width,
                justification_spacing)
            new_first_line, index = new_layout.get_first_line()
            new_first_line_width, _ = line_size(new_first_line, style)
            new_space = max_width - new_first_line_width
            hyphenated = index is None and (
                new_space >= 0 or first_word_part == dictionary_iterations[-1])
            if hyphenated:
                layout = new_layout
                first_line = new_first_line
                resume_index = len(new_first_line_text.encode())
                break

        if not hyphenated and not first_line_text:
            # Recreate the layout with no max_width to be sure that
            # we don't break before or inside the hyphenate character
            hyphenated = True
            layout.set_text(hyphenated_first_line_text)
            pango.pango_layout_set_width(layout.layout, -1)
            first_line, _ = layout.get_first_line()
            resume_index = len(new_first_line_text.encode())
            if text[len(first_line_text)] == soft_hyphen:
                resume_index += len(soft_hyphen.encode())

    if not hyphenated and first_line_text.endswith(soft_hyphen):
        # Recreate the layout with no max_width to be sure that
        # we don't break inside the hyphenate-character string
        hyphenated = True
        hyphenated_first_line_text = (
            first_line_text + style['hyphenate_character'])
        layout.set_text(hyphenated_first_line_text)
        pango.pango_layout_set_width(layout.layout, -1)
        first_line, _ = layout.get_first_line()
        resume_index = len(first_line_text.encode())

    # Step 5: Try to break word if it's too long for the line
    overflow_wrap = style['overflow_wrap']
    first_line_width, _ = line_size(first_line, style)
    space = max_width - first_line_width
    # If we can break words and the first line is too long
    can_break = (
        style['word_break'] == 'break-all' or (
            is_line_start and (
                overflow_wrap == 'anywhere' or
                (overflow_wrap == 'break-word' and not minimum))))
    if space < 0 and can_break:
        # Is it really OK to remove hyphenation for word-break ?
        hyphenated = False
        # TODO: Modify code to preserve W3C condition:
        # "Shaping characters are still shaped as if the word were not broken"
        # The way new lines are processed in this function (one by one with no
        # memory of the last) prevents shaping characters (arabic, for
        # instance) from keeping their shape when wrapped on the next line with
        # pango layout. Maybe insert Unicode shaping characters in text?
        layout.set_text(text)
        pango.pango_layout_set_width(layout.layout, int(max_width * TO_UNITS))
        pango.pango_layout_set_wrap(layout.layout, PANGO_WRAP_MODE['WRAP_CHAR'])
        first_line, index = layout.get_first_line()
        resume_index = index or first_line.length
        if resume_index >= len(text.encode()):
            resume_index = None

    return first_line_metrics(
        first_line, text, layout, resume_index, space_collapse, style,
        hyphenated, style['hyphenate_character'])
Fit as much as possible in the available width for one line of text.

Return ``(layout, length, resume_index, width, height, baseline)``.

``layout``: a pango Layout with the first line
``length``: length in UTF-8 bytes of the first line
``resume_index``: The number of UTF-8 bytes to skip for the next line.
May be ``None`` if the whole text fits in one line.
This may be greater than ``length`` in case of preserved
newline characters.
``width``: width in pixels of the first line
``height``: height in pixels of the first line
``baseline``: baseline in pixels of the first line
weasyprint/text/line_break.py