Esempio n. 1
0
    def run(self, image):
        ratio = roll_value(self.ratio)
        amount = roll_value(self.amount)
        w, h = image.size
        w = w // 4
        h = h // 4
        s_vs_p = ratio

        out = np.copy(np.array(image))
        # Salt mode
        num_salt = np.ceil(amount * w * h * s_vs_p)
        coords = [
            np.random.randint(0, i - 1, int(num_salt)) for i in image.size
        ]
        coords = tuple((coords[1], coords[0]))
        #coords = [coord[1], coord[0] for coord in coords]
        out[coords] = 255

        # Pepper mode
        num_pepper = np.ceil(amount * w * h * (1. - s_vs_p))
        coords = [
            np.random.randint(0, i - 1, int(num_pepper)) for i in image.size
        ]
        coords = tuple((coords[1], coords[0]))

        out[coords] = 0
        intermediate = PIL.ImageChops.lighter(
            image,
            PIL.Image.fromarray(out).resize(image.size, PIL.Image.CUBIC))
        return PIL.ImageChops.darker(
            intermediate,
            PIL.Image.fromarray(out).resize(image.size, PIL.Image.CUBIC))
Esempio n. 2
0
 def compose(self, img):
     n_rows = int(roll_value(self.node.get('rows')))
     n_cols = int(roll_value(self.node.get('cols')))
     schema, cell_h, cell_w = self.make_table_schema(n_rows, n_cols, self.width, self.height)
     cells = self.add_cells_to_table(schema, cell_h, cell_w)
     table = self.make_table(img, cells)
     return table
Esempio n. 3
0
    def get_font(self, text, size):
        width, height = size
        font_name = roll_value(self.f_name)

        font_data = {'name': font_name}
        for style, value in Text.style_map.items():
            if roll() < self.font.get(style, 0):
                font_data.update({style: True})
                font_name += value
        font_name = font_name.replace(' ', '_')
        f_size = roll_value(self.font_size)
        if isinstance(f_size, int):  # check if it fits
            try:
                font = PIL.ImageFont.truetype(font_name, f_size)
            except OSError:
                logging.exception(
                    f"Cannot open font {font_name} with size {f_size}")
                exit(1)
            l_width, l_height = font.getsize(text)
            c_width = l_width / len(text)
            max_chars = width // c_width

            #lines = textwrap.wrap(text, width=int(max_chars))
            if l_height > height or l_width > width:  # doesn't fit, go for filling. N.B. single line!
                f_size = 'fill'  #
                logging.debug(f"Can't fit with font size {f_size}, filling...")

        if f_size == 'fill':
            font_data.update({'filled': True})
            f_size = int(height * 0.8)

            while True:
                try:
                    font = PIL.ImageFont.truetype(font_name, f_size)
                except OSError:
                    logging.exception(
                        f"Cannot open font {font_name} with size {f_size}")
                    exit(1)
                c_width, l_height = font.getsize(text)
                c_width = c_width / len(text)
                max_chars = width // c_width
                if max_chars > 0:
                    #print(f"f_size {f_size}, max_ch {max_chars}")
                    lines = textwrap.wrap(text, width=int(max_chars))
                    if l_height < height and len(lines) == 1:
                        break

                f_size -= 1
        if f_size < self.font_min:
            return None, None
        try:
            font = PIL.ImageFont.truetype(font_name, f_size)
        except OSError:
            logging.exception(
                f"Cannot open font {font_name} with size {f_size}")
            exit(1)
        #logging.debug(f"Using font size {f_size}")
        font_data.update({'size': f_size})

        return font, font_data
Esempio n. 4
0
    def add_cells_to_table(self, schema, cell_h, cell_w):
        from generators import Component

        for cell in schema:
            #Color
            if roll() <= self.node.get('cell_p_iscolored'):
                color = (int(roll_value(self.node.get('cell_color'))),)
            else:
                color = None

            #text
            text = 'lautaro ' * 10
            if roll() <= float(self.node.get('text_p_isbold')):
                fontname = self.node.get('font') + ' Bold'
            else:
                fontname =  self.node.get('font')

            #border
            if self.node.get('zero_width') == True:
                border_width = 0
            else:
                border_width = int(roll_value(self.node.get('border_width')))

            img = Component(str(self), (self.width, self.height), self.node, background_color=(255,255,255))
            cell_istance = TableCell(cell_w, cell_h, fontname, self.base_font, self.pad_font_min_size,
                             color = color, text = text, border_width = border_width)

            img = cell_istance.generate(img)
            schema[cell] = img
        return schema
Esempio n. 5
0
    def generate(self, container_size=None, last_w=0, last_h=0):

        files_node = self.node.get('files', None)
        if files_node:
            f_path = Path(files_node['path'])

            if not f_path.exists():
                raise ValueError(f"Path {f_path} does not exist")

            paths = list(f_path.glob('*.png'))
            probabilities = files_node.get('probabilities', None)
            if probabilities:
                paths = [
                    path for path in paths
                    if path.stem in probabilities.keys()
                ]
                map = [(name, value) for name, value in probabilities.items()]
                files, probs = list(zip(*map))
                paths = sorted(
                    paths, key=lambda x: list(files).index(str(x.stem))
                )  # order Paths like zip result to keep coupling with probs
            else:
                probs = None
            file_path = random.choice(paths, p=probs)
        else:
            file_path = self.node['file']

        original = PIL.Image.open(file_path)

        size = self.get_size(container_size, last_w, last_h)
        img = Component(str(self),
                        size,
                        self.node,
                        background_color=self.background)
        w_border = roll_value(self.node.get('w_border', 0))  # %
        w_border = int(w_border * size[0])
        h_border = roll_value(self.node.get('h_border', 0))
        h_border = int(h_border * size[1])

        cropped = (size[0] - w_border), (size[1] - h_border)
        im_size = original.size

        ratio = min(cropped[0] / float(im_size[0]),
                    cropped[1] / float(im_size[1]))
        new_size = int(im_size[0] * ratio), int(im_size[1] * ratio)

        resized = original.resize(new_size, PIL.Image.ANTIALIAS)

        rand_left = roll_value([w_border, cropped[0] - resized.size[0]])
        rand_top = roll_value([h_border, cropped[1] - resized.size[1]])
        position = rand_left, rand_top
        img.annotate({'image': str(file_path), 'box': [*position, *new_size]})
        img.paste(resized, position,
                  resized.convert('RGBA'))  # use alpha mask to paste
        return img
Esempio n. 6
0
    def generate(self, container_size=None, last_w=0, last_h=0):

        size = self.get_size(container_size, last_w, last_h)

        # spoilers = self.get_spoilers()
        img = Component(str(self),
                        size,
                        self.node,
                        background_color=self.background)

        #n_lines = roll_value(self.n_lines)

        w_border = roll_value(self.node.get('w_border', 0))  # %
        w_border = int(w_border * size[0])
        h_border = roll_value(self.node.get('h_border', 0))
        h_border = int(h_border * size[1])
        width, height = cropped = (size[0] - w_border * 2), (size[1] -
                                                             h_border * 2)

        draw = ImageDraw.Draw(img)
        y = h_border

        text = next(text_gen(self.data_path, self.text))
        if roll() <= self.uppercase:
            text = text.upper()

        font, font_data = self.get_font(text, cropped)
        if font:
            fill = roll_value(self.fill)
            font_data.update({'fill': fill})

            _, l_height = font.getsize('Ag')

            align = roll_value(self.align)
            v_align = roll_value(self.v_align)
            x = w_border
            l_width, _ = draw.textsize(text, font)
            if v_align == 'bottom':
                y = height - l_height
            elif v_align == 'center':
                y = (height - l_height) // 2

            if align == 'right':
                x = width - l_width
            elif align == 'center':
                x = (width - l_width) // 2

            draw.text((x, y), text, font=font, fill=fill, align=align)
            img.annotate({
                'text': text,
                'font': font_data,
                'box': [x, y, l_width, l_height]
            })

        return img
Esempio n. 7
0
def get_position_range(component, container_size, last_x=0, last_y=0):
    parent_w, parent_h = container_size
    width, height = component.size
    position = component.node.get('position', dict())
    x = position.get('x', 0)
    y = position.get('y', 0)

    if isinstance(x, list):
        if all(isinstance(val, str) for val in x):
            x = random.choice(x)
    if isinstance(x, str):
        if x in ['head', 'left']:
            x = 0
        elif x == 'center':
            x = (parent_w - width) // 2
        elif x in ['tail', 'right']:
            x = parent_w - width
        elif x == 'concatenate':
            x = last_x
        else:
            raise ValueError(f'Unsupported position value: {x}')
        x /= parent_w  # result in % relative to parent
    if isinstance(y, list):
        if all(isinstance(val, str) for val in y):
            y = random.choice(y)
    if isinstance(y, str):
        if y in ['head', 'top']:
            y = 0
        elif y == 'center':
            y = (parent_h - height) // 2
        elif y in ['tail', 'bottom']:
            y = parent_h - height
        elif y == 'concatenate':
            y = last_y
        else:
            raise ValueError(f'Unsupported position value: {y}')
        y /= parent_h

    if isinstance(x, (float, int)):
        x = [x, x]

    if isinstance(y, (float, int)):
        y = [y, y]

    baseline_x = floor(parent_w * x[MIN])
    baseline_y = floor(parent_h * y[MIN])
    max_x = floor(x[MAX] * parent_w)
    max_y = floor(y[MAX] * parent_h)
    try:
        x = roll_value([baseline_x, max(baseline_x, max_x)])
        y = roll_value([baseline_y, max(baseline_y, max_y)])
    except ValueError as e:
        logging.warning("Illegal configuration position")

    return x, y
Esempio n. 8
0
    def run(self, image):
        grey = roll_value(self.grey)
        dilate_k = roll_value(self.dilate_k)
        logging.debug(
            f"Running TextSpoilere with grey: {grey} and kernel {dilate_k}")
        cv_im = np.array(image._img)

        kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,
                                           (dilate_k, dilate_k))
        dilated = cv2.morphologyEx(cv_im, cv2.MORPH_ERODE, kernel)
        dilated[dilated < 120] = grey
        pil_im = PIL.Image.fromarray(dilated)
        image._img = pil_im
        return image
Esempio n. 9
0
 def run(self, image):
     quality = roll_value(self.quality)
     subsampling = roll_value(self.subsampling)
     logging.debug(
         f"Running JPEGCompression with quality: {quality} and subsampling {subsampling}"
     )
     compressed = BytesIO()
     image.save(compressed,
                "JPEG",
                quality=quality,
                subsampling=subsampling)
     compressed.seek(0)
     compr = PIL.Image.open(compressed)
     image._img = compr
     return image
Esempio n. 10
0
 def run(self, image):
     kernel = self.get_kernel()
     k = roll_value(self.k)
     data = {'type': self.type(), 'k': k}
     self.annotate(image, data)
     #image = cv2.erode(np.array(image),kernel,iterations=2)
     return image.filter(PIL.ImageFilter.MaxFilter(self.k))
Esempio n. 11
0
def text_gen(file=None, value=None):
    if file:
        file_size = os.path.getsize(file)
        offset = roll_value([1, file_size])
        with open(file, 'r') as file:
            file.seek(offset)
            try:
                file.readline()
            except:
                pass
            while True:
                try:
                    line = file.readline()
                except Exception as e:
                    print(e)
                    continue
                if not line:
                    file.seek(0, 0)
                    continue
                yield line
    else:
        while True:
            text = value or "PLACEHOLDER TEXT"
            for line in text.split('\n'):
                yield line
Esempio n. 12
0
 def run(self, image):
     logging.debug(f"Running Foreground with grey {self.grey}")
     w, h = image.size
     grid_ratio = roll_value(self.grid_ratio)
     noise = _white_noise(w, h, self.grey, grid_ratio=grid_ratio)
     data = {'type': self.type(), 'grey': self.grey}
     self.annotate(image, data)
     return PIL.ImageChops.lighter(image, noise)
Esempio n. 13
0
    def generate(self, container_size=None, last_w=0, last_h=0):
        size = self.get_size(container_size, last_w, last_h)

        cell = Component(str(self),
                         size,
                         self.node,
                         background_color=self.background)
        frame_size = roll_value(self.frame)
        frame = self.add_frame(cell, frame_size)
        cell = self.populate(cell, frame)
        cell.annotate({'frame': frame, 'title': self.is_title})
        return cell
Esempio n. 14
0
    def run(self, image):
        n = roll_value(self.n)
        logging.debug(f"Running Pad with n:{n}")

        w, h = image.size
        data = {'type': self.type(), 'n': n}
        self.annotate(image, data)
        draw = PIL.ImageDraw.Draw(image)
        draw.rectangle((0, 0, w, h), width=n)

        # bg = PIL.Image.new(image.mode, (w + 2 * n, h + 2 * n), 0)
        # bg.paste(image, (n, n))
        return image
Esempio n. 15
0
 def run(self, image):
     logging.debug(f"Running WhiteNoise")
     cv2_im = np.array(image._img)
     mask = np.where(cv2_im != 255)  # get black pixels
     mask = np.array(list(zip(*mask)))  # couples
     n_pixels = len(mask)
     ratio = roll_value(self.ratio)
     idx = np.random.choice(
         len(mask), int(n_pixels * ratio),
         replace=False)  # random choice pixels to set white
     mask = mask[idx]
     mask = tuple(zip(*mask))
     cv2_im[mask] = 255
     image.update(PIL.Image.fromarray(cv2_im))
     return image
Esempio n. 16
0
    def __init__(self, width, height, node):
        self.height = height
        self.width = width
        self.node = node

        # --- general parameters which should be common for all configurations
        self.min_font = int(node.get('font_min_size', 14))  # 13
        self.base_font = max(self.min_font, self.height // 80)
        self.pad_font_min_size = int(roll_value(node.get('font_delta_size', 6)))
        # ---

        keys_file = node.get('keys_path', None)
        assert keys_file
        with open(keys_file, 'r') as f:
            self.textheads = f.read()
            self.textheads = self.textheads.split('\n')

        self.im = Image.new("L", (self.width, self.height), 255)
Esempio n. 17
0
 def run(self, image):
     angle = round(roll_value(self.angle), 1)
     alpha = math.pi * angle / 180
     c = abs(math.cos(alpha))
     s = abs(math.sin(alpha))
     logging.debug(f"Running Rotate with angle {angle}")
     #TODO check this
     #rotated = Pad(100).run(image).convert('RGBA').rotate(self.angle, expand = 1)
     rotated = image.convert('RGBA').rotate(
         angle,
         resample=PIL.Image.BICUBIC,
         expand=True,
         fillcolor='white',
     )
     box = self.center_box(rotated.size, image.size, c, s)
     data = {'type': self.type(), 'angle': angle}
     self.annotate(image, data)
     return rotated.crop(box)
Esempio n. 18
0
def roll_table_sizes(table, splits, axis):
    # N.B. axis is the axis to use for varying values e.g. 0 -> varying widths
    # trying to keep the values coupled to enable swap based on axis
    dims = table.size
    measure = dims[axis]  # width
    mu = measure / splits  # width / n_cols

    #fixed_side = dims[opposite_axis] // grid[opposite_axis]  # height // n_rows

    rem = measure  # width
    min = mu * MIN_RATIO
    max_ = mu * MAX_RATIO
    sigma = mu * 0.3
    sizes = []

    #  create 1 row/col sizes, calculating varying width/height, permute if axis == 1
    for i in range(splits - 1):
        var_side = int(
            roll_value({
                'distribution': 'normal',
                'mu': mu,
                'sigma': sigma,
                'min': min,
                'max': max_
            }))
        rem -= var_side

        mu = rem / (splits - (i + 1))
        max_ = int(mu * MAX_RATIO)
        min = int(mu * MIN_RATIO)

        # size = var_side, fixed_side
        # # if axis == 1:  # transpose if rolling heights
        # size = size[axis], size[opposite_axis]
        sizes.append(var_side)

    # === last cell
    sizes.append(rem)  # fill with last cell
    return sizes
Esempio n. 19
0
    def generate(self, container_size=None, last_w=0, last_h=0):
        """Runs sub-elements generation and computes positions based on the config parameters"""

        size = self.get_size(container_size, last_w, last_h)

        logging.debug(f"Generating container with size {size}")

        img = Component(str(self), size, self.node)
        #available_x, available_y = width, height = size
        # total_units = 100
        # unit = (height // total_units)
        probs = [gen.p for gen in self.generators]
        if sum(probs) != 1:
            probs = None  # undefined p, using uniform

        chosen = roll_value(list(zip(self.generators, probs)))

        im = chosen.generate(size)
        #node = chosen.node
        x, y = get_position_range(im, container_size)
        img.add(im, (x, y))
        img.render()
        return img
Esempio n. 20
0
 def run(self, image):
     if image.type == 'TableCell' and image.node.get(
             'is_key',
             None) is None:  #or image.node.get('invert', None) is not None:
         elements = [(image, None)]
     elif image.type == 'Table' and image.node.get('invert', None) is None:
         elements = [
             cell for cell in image.elements
             if cell[0].node.get('is_key', None) is not None
         ]
         image.node['modified_bg'] = True
     else:
         return image
     logging.debug(f"Running Cell Background with grey {self.grey}")
     grid_ratio = roll_value(self.grid_ratio)
     for el in elements:
         w, h = el[0].size
         noise = _white_noise(w, h, self.grey, grid_ratio)
         cell_with_noise = PIL.ImageChops.darker(el[0], noise)
         if image.type == 'TableCell':
             image._img = cell_with_noise
         else:
             image.paste(cell_with_noise, el[1])
     return image
Esempio n. 21
0
        def run(self, image):
            logging.debug(f"Running WhiteNoise")
            cv2_im = np.array(image._img)
            mask = np.where(cv2_im != 255)  # get black pixels
            mask = np.array(list(zip(*mask)))  # couples
            n_pixels = len(mask)
            ratio = roll_value(self.ratio)
            idx = np.random.choice(
                len(mask), int(n_pixels * ratio),
                replace=False)  # random choice pixels to set white
            mask = mask[idx]
            mask = tuple(zip(*mask))
            cv2_im[mask] = 255
            image.update(PIL.Image.fromarray(cv2_im))
            return image

    #
    # @staticmethod
    # def random():
    #     # if random.randint(0, 10) < 7:
    #     #     return Filter()
    #     # else:
    #     grad = random.randint(1, 10)
    #     dir = random.randint(0, 1)
    #     color = random.randint(100, 200)
    #     return Gradient(grad/10, dir, color)


# def filters_from_cfg(cfg):
#     filters = [
#         Pad.random(),
#         Foreground.random(),
#         Blur.random(),
#         Stroke.random(),
#         VerticalLine.random(),
#         Overlay.random('resources/stamps', (100, 100)),
#         Gradient.random(),
#         Rotate.random(),
#         Background.random()
#     ]
#     return filters
#
# def spoil(im, options):
#     im = Crop.random().run(im)
#     filters = [
#         VerticalLine.random(),
#         Overlay.random('resources/stamps', (100, 100)),
#         Rotate.random() if not options.no_skew else Filter(),
#         Pad.random(),
#         Background.random(),
#         Foreground.random(),
#         Stroke.random(),
#         Blur.random(),
#         #Gradient.random()
#     ]
#
#     noisy = im
#     for f in filters:
#         noisy = f.run(noisy)
#
#     return noisy
Esempio n. 22
0
    def generate(self, container_size=None, last_w=0, last_h=0):

        size = self.get_size(container_size, last_w, last_h)

        img = Component(str(self), size, self.node)

        n_lines = roll_value(self.n_lines)

        w_border = roll_value(self.node.get('w_border', 0))  # %
        w_border = int(w_border * size[0])
        h_border = roll_value(self.node.get('h_border', 0))
        h_border = int(h_border * size[1])
        cropped = (size[0] - w_border * 2), (size[1] - h_border * 2)

        font_name = roll_value(self.f_name)
        font_data = {'name': font_name}

        for style, value in TextGroup.style_map.items():
            if roll() < self.font.get(style, 0):
                font_data.update({style: True})
                font_name += value

        font_name = font_name.replace(' ', '_')
        f_size = roll_value(self.font_size)
        try:
            font = PIL.ImageFont.truetype(font_name, f_size)
        except OSError:
            logging.exception(
                f"Cannot open font {font_name} with size {f_size}")
            exit(1)
        width, l_height = font.getsize('Ag')
        while l_height + h_border > cropped[1]:
            f_size -= 1
            font = PIL.ImageFont.truetype(font_name, f_size)
            width, l_height = font.getsize('A g')

        font_data.update({'size': f_size})
        draw = ImageDraw.Draw(img)
        y = h_border
        width = int(cropped[0] // width * 2)
        texts = text_gen(self.data_path)
        fill = roll_value(self.fill)
        font_data.update({'fill': fill})
        x = w_border
        x0, y0 = x, y
        x1 = 0
        text_data = []
        while y + l_height <= cropped[1] and n_lines != 0:

            for line in textwrap.wrap(next(texts), width=width):
                l_width, l_height = font.getsize(line)
                x1 = max(x1, l_width)
                if y + l_height > cropped[1] or n_lines == 0:
                    break
                draw.text((x, y), line, font=font, fill=fill)
                n_lines -= 1
                y += l_height
                text_data.append({
                    'text': line,
                    'font': font_data,
                    'box': [x, y, width, l_height]
                })
        img.annotate({'box': [x0, y0, x1, y], 'text_data': text_data})

        return img
Esempio n. 23
0
    def populate(self, cell, frame):

        size = cell.size
        # -- white border
        w_border = roll_value(self.w_border)  # in %
        l_border = int(w_border * size[0]) + frame[0]
        r_border = int(w_border * size[0]) + frame[2]
        h_border = roll_value(self.h_border)
        t_border = int(h_border * size[1]) + frame[1]
        b_border = int(h_border * size[1]) + frame[3]
        size = width, height = (size[0] - l_border -
                                r_border), (size[1] - t_border - b_border)

        if roll() <= self.key_p:
            axis_split = roll_axis_split(width,
                                         height)  # 1 for horizontal split
            opposite_ax = abs(axis_split - 1)
            split_size = int(size[axis_split] * roll_value(
                [0.3, 0.7])), size[opposite_ax]  # calc random split on side
            width_key, height_key = split_size[axis_split], split_size[
                opposite_ax]  # permute if axis == 1

            key_node = {
                'Text': {
                    'size': {
                        'width': width_key,
                        'height': height_key
                    },
                    'source_path': self.keys_file,
                    'n_lines': 1,
                    'uppercase': self.key_upper,
                    'font': self.key_font
                }
            }

            key_gen = Text(key_node)
            key = key_gen.generate(container_size=size)
            if key.empty():
                pass
                #return cell
            key.annotate({'value': False, 'key': True, 'axis': axis_split})
            cell.add(key, (l_border, t_border))
            width = size[0] - (width_key * opposite_ax
                               )  # keep side intact or decrement based on axis
            height = size[1] - (height_key * axis_split)
            l_border = (l_border * axis_split) + (
                size[0] - width +
                l_border) * opposite_ax  # calc offset where to place cell
            t_border = (t_border * opposite_ax) + (size[1] - height +
                                                   t_border) * axis_split
        # Creating text generator with calculated size, default alignment and my font info
        value_node = {
            'Text': {
                'size': {
                    'width': width,
                    'height': height
                },
                'source_path': self.values_file,
                'n_lines': 1,
                'background_color': self.background,
                'uppercase': self.value_upper,
                'font': self.value_font
            }
        }

        value_gen = Text(value_node)
        value = value_gen.generate(container_size=size)
        value.annotate({'value': True, 'key': False})
        cell.add(value, (l_border, t_border))
        cell.render()
        return cell
Esempio n. 24
0
 def run(self, image):
     r = roll_value(self.r)
     logging.debug(f"Running Blur with radius {r}")
     data = {'type': self.type(), 'r': r}
     self.annotate(image, data)
     return image.filter(PIL.ImageFilter.GaussianBlur(r))
Esempio n. 25
0
    def make_schema(self, table):
        """Create a matrix row x cols with (Cell_node, position)"""
        n_cols = roll_value(self.cols)
        n_rows = roll_value(self.rows)
        if roll() <= self.fix_cols:  # fixed dims
            cell_w = table.width // n_cols
            widths = [cell_w] * n_cols
        else:
            widths = roll_table_sizes(table, n_cols, axis=0)
        if roll() <= self.fix_rows:
            cell_h = table.height // n_rows
            heights = [cell_h] * n_rows
        else:
            heights = roll_table_sizes(table, n_rows, axis=1)

        cell_node = {
            'size': {
                'width': 0,
                'height': 0
            },
            'value': {
                'file': self.values_file,
                'font': self.font
            },
            'key': {
                'p': 0.5,
                'file': self.keys_file,
                'font': self.font
            },
            'cell_borders': [],  # to be filled later on
            'spoilers': self.cell_spoilers
        }
        pos_mapping = list()
        row_idx = col_idx = 0
        if roll() <= self.title:  # create title, one row single cell
            title_node = deepcopy(cell_node)
            h = heights[0]
            del title_node['key']
            title_node['value'].update(file=self.title_file,
                                       font={'size': 'fill'})
            title_node.update(size={
                'width': table.width,
                'height': h
            },
                              is_title=True)
            pos_mapping.append([({
                'TableCell': deepcopy(title_node)
            }, (0, 0))])  # first row
            row_idx = 1

        if roll() <= self.fix_keys_col:  # create keys row

            h = heights[row_idx]
            y = sum(heights[:row_idx])

            key_node = deepcopy(cell_node)
            del key_node['key']

            row = list()
            for j in range(len(widths)):
                x = sum(widths[:j])
                w = widths[j]
                position = x, y
                key_node.update(size={'width': w, 'height': h}, is_key=True)
                key_node['value'].update(file=self.keys_file, uppercase=0.8)
                row.append(({'TableCell': deepcopy(key_node)}, position))
            row_idx += 1
            pos_mapping.append(row)

        # if roll() <= self.fix_keys_row:
        #     w = widths[col_idx]
        #     x = sum(widths[:col_idx]) # 0
        #     key_node = deepcopy(cell_node)
        #     del key_node['key']
        #     col = list()
        #     for i in range(len(heights)):

        while row_idx < len(heights):
            row = []

            for j in range(len(widths)):
                x = sum(widths[:j])
                y = sum(heights[:row_idx])
                w = widths[j]
                h = heights[row_idx]
                position = x, y
                cell_node.update(size={'width': w, 'height': h}, is_val=True)
                row.append(({'TableCell': deepcopy(cell_node)}, position))

            pos_mapping.append(row)
            row_idx += 1

        return pos_mapping