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
def put_borders(self, schema): """ Take a table schema and fill borders based on params N.B. this method works inplace thus modifies the original schema""" def add_bs(bs, nodes): for node in nodes: if node is None: continue borders = node['TableCell']['cell_borders'] for b in bs: if b not in borders: borders.append(b) # replicate first cell if row has less cells (title) np_schema = np.array( [row + [row[0]] * (len(schema[-1]) - len(row)) for row in schema], dtype=object) first_row = np_schema[ 0, :, 0] # create list from first row, every column, first couple element first_col = np_schema[:, 0, 0] last_row = np_schema[-1, :, 0] last_col = np_schema[:, -1, 0] # externals first_row_borders = ['top'] if first_row[0]['TableCell'].get( 'is_title', False ) or first_row[0]['TableCell'].get( 'is_key', False ): # if is title and then no row frame the title must be enclosed first_row_borders.append('bottom') add_bs(first_row_borders, first_row) add_bs(['left'], first_col) add_bs(['bottom'], last_row) add_bs(['right'], last_col) internal_borders = [] if roll() <= self.row_frame: internal_borders.append('bottom') if roll() <= self.col_frame: internal_borders.append('right') add_bs(internal_borders, np_schema[:, :, 0].flatten()) # add every cell right border return schema
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
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.info(f"Generating image with size {size}") img = Component(str(self), size, self.node) # create component with rolled size #available_x, available_y = width, height = size # total_units = 100 # unit = (height // total_units) last_x2 = last_y2 = 0 for gen in self.generators: # iterate over sub_generators if roll() > gen.p: continue # skip gen try: component = gen.generate(size, last_x2, last_y2) # generate component except Exception as e: logging.exception(f"Problems generating {gen} {gen.node}") break #node = gen.node x, y = get_position_range(component, size, last_x2, last_y2) x, y = img.check_position_for(x, y, component) last_x2 = max(x + component.size[0], last_x2) last_y2 = max(y + component.size[1], last_y2) # available_x -= x - baseline_x # available_y -= y - baseline_y img.add(component, (x, y)) # paste generated component img.render() return img
def roll_and_run(self, image: Component): """Rolls and eventually applies the filter""" if roll() <= self.p: img = self.run(image) for filter in self.exclude: image_spoilers = image.node.get('spoilers', dict()) image_spoilers.get(filter, { 'p': 0 }).update(p=0) # clear filter probability return img
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
def roll_axis_split(width, height): # for key value positioning """ Calculating w-h / w+h which belongs to ]-1, 1[ , then normalize in ]0,1[ as p --> w / (w+h)/2 """ # augment height to make horizontal split more likely to be rolled (like most table cells) height = height + (height * HORIZ_BIAS) side_ratio = (width + height) / 2 p_horizontal = (height / side_ratio) if roll() <= p_horizontal: return 1 return 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
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
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
def test2(): random.seed(14) assert student.roll(5, 10) == 30
def test1(): random.seed(0) assert student.roll(2, 6) == 8
def test3(): random.seed(10) assert student.roll(10, 6) == 33