<commit_before>"""
    weasyprint.draw
    ---------------

    Take an "after layout" box tree and draw it onto a cairo context.

"""

import contextlib
import operator
from math import ceil, floor, hypot, pi, sqrt, tan

import cairocffi as cairo

from .formatting_structure import boxes
from .images import SVGImage
from .layout import replaced
from .layout.backgrounds import BackgroundLayer
from .stacking import StackingContext
from .text import show_first_line

SIDES = ('top', 'right', 'bottom', 'left')
CROP = '''
  <!-- horizontal top left -->
  <path d="M0,{bleed_top} h{half_bleed_left}" />
  <!-- horizontal top right -->
  <path d="M0,{bleed_top} h{half_bleed_right}"
        transform="translate({width},0) scale(-1,1)" />
  <!-- horizontal bottom right -->
  <path d="M0,{bleed_bottom} h{half_bleed_right}"
        transform="translate({width},{height}) scale(-1,-1)" />
  <!-- horizontal bottom left -->
  <path d="M0,{bleed_bottom} h{half_bleed_left}"
        transform="translate(0,{height}) scale(1,-1)" />
  <!-- vertical top left -->
  <path d="M{bleed_left},0 v{half_bleed_top}" />
  <!-- vertical bottom right -->
  <path d="M{bleed_right},0 v{half_bleed_bottom}"
        transform="translate({width},{height}) scale(-1,-1)" />
  <!-- vertical bottom left -->
  <path d="M{bleed_left},0 v{half_bleed_bottom}"
        transform="translate(0,{height}) scale(1,-1)" />
  <!-- vertical top right -->
  <path d="M{bleed_right},0 v{half_bleed_top}"
        transform="translate({width},0) scale(-1,1)" />
'''
CROSS = '''
  <!-- top -->
  <circle r="{half_bleed_top}"
          transform="scale(0.5)
                     translate({width},{half_bleed_top}) scale(0.5)" />
  <path d="M-{half_bleed_top},{half_bleed_top} h{bleed_top}
           M0,0 v{bleed_top}"
        transform="scale(0.5) translate({width},0)" />
  <!-- bottom -->
  <circle r="{half_bleed_bottom}"
          transform="translate(0,{height}) scale(0.5)
                     translate({width},-{half_bleed_bottom}) scale(0.5)" />
  <path d="M-{half_bleed_bottom},-{half_bleed_bottom} h{bleed_bottom}
           M0,0 v-{bleed_bottom}"
        transform="translate(0,{height}) scale(0.5) translate({width},0)" />
  <!-- left -->
  <circle r="{half_bleed_left}"
          transform="scale(0.5)
                     translate({half_bleed_left},{height}) scale(0.5)" />
  <path d="M{half_bleed_left},-{half_bleed_left} v{bleed_left}
           M0,0 h{bleed_left}"
        transform="scale(0.5) translate(0,{height})" />
  <!-- right -->
  <circle r="{half_bleed_right}"
          transform="translate({width},0) scale(0.5)
                     translate(-{half_bleed_right},{height}) scale(0.5)" />
  <path d="M-{half_bleed_right},-{half_bleed_right} v{bleed_right}
           M0,0 h-{bleed_right}"
        transform="translate({width},0)
                   scale(0.5) translate(0,{height})" />
'''


@contextlib.contextmanager
def stacked(context):
    """Save and restore the context when used with the ``with`` keyword."""
    context.save()
    try:
        yield
    finally:
        context.restore()


def hsv2rgb(hue, saturation, value):
    """Transform a HSV color to a RGB color."""
    c = value * saturation
    x = c * (1 - abs((hue / 60) % 2 - 1))
    m = value - c
    if 0 <= hue < 60:
        return c + m, x + m, m
    elif 60 <= hue < 120:
        return x + m, c + m, m
    elif 120 <= hue < 180:
        return m, c + m, x + m
    elif 180 <= hue < 240:
        return m, x + m, c + m
    elif 240 <= hue < 300:
        return x + m, m, c + m
    elif 300 <= hue < 360:
        return c + m, m, x + m


def rgb2hsv(red, green, blue):
    """Transform a RGB color to a HSV color."""
    cmax = max(red, green, blue)
    cmin = min(red, green, blue)
    delta = cmax - cmin
    if delta == 0:
        hue = 0
    elif cmax == red:
        hue = 60 * ((green - blue) / delta % 6)
    elif cmax == green:
        hue = 60 * ((blue - red) / delta + 2)
    elif cmax == blue:
        hue = 60 * ((red - green) / delta + 4)
    saturation = 0 if delta == 0 else delta / cmax
    return hue, saturation, cmax


def get_color(style, key):
    value = style[key]
    return value if value != 'currentColor' else style['color']


def darken(color):
    """Return a darker color."""
    hue, saturation, value = rgb2hsv(color.red, color.green, color.blue)
    value /= 1.5
    saturation /= 1.25
    return hsv2rgb(hue, saturation, value) + (color.alpha,)


def lighten(color):
    """Return a lighter color."""
    hue, saturation, value = rgb2hsv(color.red, color.green, color.blue)
    value = 1 - (1 - value) / 1.5
    if saturation:
        saturation = 1 - (1 - saturation) / 1.25
    return hsv2rgb(hue, saturation, value) + (color.alpha,)


def draw_page(page, context, enable_hinting):
    """Draw the given PageBox."""
    bleed = {
        side: page.style['bleed_%s' % side].value
        for side in ('top', 'right', 'bottom', 'left')}
    marks = page.style['marks']
    stacking_context = StackingContext.from_page(page)
    draw_background(
        context, stacking_context.box.background, enable_hinting,
        clip_box=False, bleed=bleed, marks=marks)
    draw_background(
        context, page.canvas_background, enable_hinting, clip_box=False)
    draw_border(context, page, enable_hinting)
    draw_stacking_context(context, stacking_context, enable_hinting)


def draw_box_background_and_border(context, page, box, enable_hinting):
    draw_background(context, box.background, enable_hinting)
    if isinstance(box, boxes.TableBox):
        draw_table_backgrounds(context, page, box, enable_hinting)
        if box.style['border_collapse'] == 'separate':
            draw_border(context, box, enable_hinting)
            for row_group in box.children:
                for row in row_group.children:
                    for cell in row.children:
                        if (cell.style['empty_cells'] == 'show' or
                                not cell.empty):
                            draw_border(context, cell, enable_hinting)
        else:
            draw_collapsed_borders(context, box, enable_hinting)
    else:
        draw_border(context, box, enable_hinting)


def draw_stacking_context(context, stacking_context, enable_hinting):
    """Draw a ``stacking_context`` on ``context``."""
    # See http://www.w3.org/TR/CSS2/zindex.html
    with stacked(context):
        box = stacking_context.box

        # apply the viewport_overflow to the html box, see #35
        if box.element_tag == 'html' and (
                stacking_context.page.style['overflow'] != 'visible'):
            rounded_box_path(
                context,
                stacking_context.page.rounded_padding_box())
            context.clip()

        if box.is_absolutely_positioned() and box.style['clip']:
            top, right, bottom, left = box.style['clip']
            if top == 'auto':
                top = 0
            if right == 'auto':
                right = 0
            if bottom == 'auto':
                bottom = box.border_height()
            if left == 'auto':
                left = box.border_width()
            context.rectangle(
                box.border_box_x() + right,
                box.border_box_y() + top,
                left - right,
                bottom - top)
            context.clip()

        if box.style['opacity'] < 1:
            context.push_group()
<commit_msg>Apply viewport_overflow when box.is_for_root_element<commit_after>"""
    weasyprint.draw
    ---------------

    Take an "after layout" box tree and draw it onto a cairo context.

"""

import contextlib
import operator
from math import ceil, floor, hypot, pi, sqrt, tan

import cairocffi as cairo

from .formatting_structure import boxes
from .images import SVGImage
from .layout import replaced
from .layout.backgrounds import BackgroundLayer
from .stacking import StackingContext
from .text import show_first_line

SIDES = ('top', 'right', 'bottom', 'left')
CROP = '''
  <!-- horizontal top left -->
  <path d="M0,{bleed_top} h{half_bleed_left}" />
  <!-- horizontal top right -->
  <path d="M0,{bleed_top} h{half_bleed_right}"
        transform="translate({width},0) scale(-1,1)" />
  <!-- horizontal bottom right -->
  <path d="M0,{bleed_bottom} h{half_bleed_right}"
        transform="translate({width},{height}) scale(-1,-1)" />
  <!-- horizontal bottom left -->
  <path d="M0,{bleed_bottom} h{half_bleed_left}"
        transform="translate(0,{height}) scale(1,-1)" />
  <!-- vertical top left -->
  <path d="M{bleed_left},0 v{half_bleed_top}" />
  <!-- vertical bottom right -->
  <path d="M{bleed_right},0 v{half_bleed_bottom}"
        transform="translate({width},{height}) scale(-1,-1)" />
  <!-- vertical bottom left -->
  <path d="M{bleed_left},0 v{half_bleed_bottom}"
        transform="translate(0,{height}) scale(1,-1)" />
  <!-- vertical top right -->
  <path d="M{bleed_right},0 v{half_bleed_top}"
        transform="translate({width},0) scale(-1,1)" />
'''
CROSS = '''
  <!-- top -->
  <circle r="{half_bleed_top}"
          transform="scale(0.5)
                     translate({width},{half_bleed_top}) scale(0.5)" />
  <path d="M-{half_bleed_top},{half_bleed_top} h{bleed_top}
           M0,0 v{bleed_top}"
        transform="scale(0.5) translate({width},0)" />
  <!-- bottom -->
  <circle r="{half_bleed_bottom}"
          transform="translate(0,{height}) scale(0.5)
                     translate({width},-{half_bleed_bottom}) scale(0.5)" />
  <path d="M-{half_bleed_bottom},-{half_bleed_bottom} h{bleed_bottom}
           M0,0 v-{bleed_bottom}"
        transform="translate(0,{height}) scale(0.5) translate({width},0)" />
  <!-- left -->
  <circle r="{half_bleed_left}"
          transform="scale(0.5)
                     translate({half_bleed_left},{height}) scale(0.5)" />
  <path d="M{half_bleed_left},-{half_bleed_left} v{bleed_left}
           M0,0 h{bleed_left}"
        transform="scale(0.5) translate(0,{height})" />
  <!-- right -->
  <circle r="{half_bleed_right}"
          transform="translate({width},0) scale(0.5)
                     translate(-{half_bleed_right},{height}) scale(0.5)" />
  <path d="M-{half_bleed_right},-{half_bleed_right} v{bleed_right}
           M0,0 h-{bleed_right}"
        transform="translate({width},0)
                   scale(0.5) translate(0,{height})" />
'''


@contextlib.contextmanager
def stacked(context):
    """Save and restore the context when used with the ``with`` keyword."""
    context.save()
    try:
        yield
    finally:
        context.restore()


def hsv2rgb(hue, saturation, value):
    """Transform a HSV color to a RGB color."""
    c = value * saturation
    x = c * (1 - abs((hue / 60) % 2 - 1))
    m = value - c
    if 0 <= hue < 60:
        return c + m, x + m, m
    elif 60 <= hue < 120:
        return x + m, c + m, m
    elif 120 <= hue < 180:
        return m, c + m, x + m
    elif 180 <= hue < 240:
        return m, x + m, c + m
    elif 240 <= hue < 300:
        return x + m, m, c + m
    elif 300 <= hue < 360:
        return c + m, m, x + m


def rgb2hsv(red, green, blue):
    """Transform a RGB color to a HSV color."""
    cmax = max(red, green, blue)
    cmin = min(red, green, blue)
    delta = cmax - cmin
    if delta == 0:
        hue = 0
    elif cmax == red:
        hue = 60 * ((green - blue) / delta % 6)
    elif cmax == green:
        hue = 60 * ((blue - red) / delta + 2)
    elif cmax == blue:
        hue = 60 * ((red - green) / delta + 4)
    saturation = 0 if delta == 0 else delta / cmax
    return hue, saturation, cmax


def get_color(style, key):
    value = style[key]
    return value if value != 'currentColor' else style['color']


def darken(color):
    """Return a darker color."""
    hue, saturation, value = rgb2hsv(color.red, color.green, color.blue)
    value /= 1.5
    saturation /= 1.25
    return hsv2rgb(hue, saturation, value) + (color.alpha,)


def lighten(color):
    """Return a lighter color."""
    hue, saturation, value = rgb2hsv(color.red, color.green, color.blue)
    value = 1 - (1 - value) / 1.5
    if saturation:
        saturation = 1 - (1 - saturation) / 1.25
    return hsv2rgb(hue, saturation, value) + (color.alpha,)


def draw_page(page, context, enable_hinting):
    """Draw the given PageBox."""
    bleed = {
        side: page.style['bleed_%s' % side].value
        for side in ('top', 'right', 'bottom', 'left')}
    marks = page.style['marks']
    stacking_context = StackingContext.from_page(page)
    draw_background(
        context, stacking_context.box.background, enable_hinting,
        clip_box=False, bleed=bleed, marks=marks)
    draw_background(
        context, page.canvas_background, enable_hinting, clip_box=False)
    draw_border(context, page, enable_hinting)
    draw_stacking_context(context, stacking_context, enable_hinting)


def draw_box_background_and_border(context, page, box, enable_hinting):
    draw_background(context, box.background, enable_hinting)
    if isinstance(box, boxes.TableBox):
        draw_table_backgrounds(context, page, box, enable_hinting)
        if box.style['border_collapse'] == 'separate':
            draw_border(context, box, enable_hinting)
            for row_group in box.children:
                for row in row_group.children:
                    for cell in row.children:
                        if (cell.style['empty_cells'] == 'show' or
                                not cell.empty):
                            draw_border(context, cell, enable_hinting)
        else:
            draw_collapsed_borders(context, box, enable_hinting)
    else:
        draw_border(context, box, enable_hinting)


def draw_stacking_context(context, stacking_context, enable_hinting):
    """Draw a ``stacking_context`` on ``context``."""
    # See http://www.w3.org/TR/CSS2/zindex.html
    with stacked(context):
        box = stacking_context.box

        # apply the viewport_overflow to the html box, see #35
        if box.is_for_root_element and (
                stacking_context.page.style['overflow'] != 'visible'):
            rounded_box_path(
                context,
                stacking_context.page.rounded_padding_box())
            context.clip()

        if box.is_absolutely_positioned() and box.style['clip']:
            top, right, bottom, left = box.style['clip']
            if top == 'auto':
                top = 0
            if right == 'auto':
                right = 0
            if bottom == 'auto':
                bottom = box.border_height()
            if left == 'auto':
                left = box.border_width()
            context.rectangle(
                box.border_box_x() + right,
                box.border_box_y() + top,
                left - right,
                bottom - top)
            context.clip()

        if box.style['opacity'] < 1:
            context.push_group()

{'fraction_non_alphanumeric': 0.1478915865222947, 'fraction_numerical': 0.018965633196583495, 'mean_word_length': 2.430219146482122, 'pattern_counts': {'":': 0, '<': 85, '<?xml version=': 0, '>': 59, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1025599', 'n_tokens_mistral': 4997, 'n_tokens_neox': 4684, 'n_words': 1162}