Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # tk_rtf_tablesheet.py
- from tkinter import Tk, Canvas
- from PIL import Image, ImageDraw, ImageTk, ImageFont
- import textwrap
- import os
- import re
- rtf_control = r'''
- {\rtf1\ansi\deff0
- {\fonttbl
- {\f0\fswiss Arial;}
- {\f1\fswiss Arial Bold;}
- }
- {\colortbl;\red0\green0\blue0;\red255\green255\blue255;\red255\green255\blue0;\red0\green255\blue0;}
- \fs28\b\cf4 RAW Power\line Retail And Wholesale\par
- \fs20\b\cf1
- {\trowd\trgaph100\trleft-100
- \clbrdrb\brdrs\brdrw10\clcbpat3\cellx2000
- \clbrdrb\brdrs\brdrw10\clcbpat3\cellx4000
- \clbrdrb\brdrs\brdrw10\clcbpat3\cellx6000
- \clbrdrb\brdrs\brdrw10\clcbpat3\cellx8000
- \pard\intbl Items\cell 1 to 9\cell 10 to 19\cell 20+\cell\row}
- \fs20\cf1
- {\trowd\trgaph100\trleft-100
- \clbrdrb\brdrs\brdrw10\clcbpat4\cellx2000
- \clbrdrb\brdrs\brdrw10\clcbpat4\cellx4000
- \clbrdrb\brdrs\brdrw10\clcbpat4\cellx6000
- \clbrdrb\brdrs\brdrw10\clcbpat4\cellx8000
- \pard\intbl T-Shirts\cell $10.00\cell $5.00\cell $1.00\cell\row}
- {\trowd\trgaph100\trleft-100
- \clbrdrb\brdrs\brdrw10\clcbpat4\cellx2000
- \clbrdrb\brdrs\brdrw10\clcbpat4\cellx4000
- \clbrdrb\brdrs\brdrw10\clcbpat4\cellx6000
- \clbrdrb\brdrs\brdrw10\clcbpat4\cellx8000
- \pard\intbl Jeans\cell $25.00\cell $15.00... and this is to test if the textwrap also works\cell $5.00\cell\row}
- {\trowd\trgaph100\trleft-100
- \clbrdrb\brdrs\brdrw10\clcbpat4\cellx2000
- \clbrdrb\brdrs\brdrw10\clcbpat4\cellx4000
- \clbrdrb\brdrs\brdrw10\clcbpat4\cellx6000
- \clbrdrb\brdrs\brdrw10\clcbpat4\cellx8000
- \pard\intbl Jackets\cell $100.00\cell $70.00\cell $40.00\cell\row}
- }
- '''
- def get_font(font_name="arial", fontsize=16, style="regular", font_dir="fonts"):
- FONT_STYLES = {"regular": "", "bold": "bd", "italic": "i", "bold_italic": "bi"}
- style = FONT_STYLES.get(style.lower(), "")
- font_file = f"{font_name}{style}.ttf"
- font_path = os.path.join(font_dir, font_file)
- return ImageFont.truetype(font_path, fontsize)
- def clean_rtf_text(text):
- text = re.sub(r'\\\$', '$', text)
- text = re.sub(r'\\[a-z]+\d*', '', text)
- text = re.sub(r'[{}]', '', text)
- return text.strip()
- def get_tables(rtf_text):
- below = rtf_text
- tables = []
- color_table = []
- if r'\colortbl' in rtf_text:
- colors = re.findall(r'\\red(\d+)\\green(\d+)\\blue(\d+)', rtf_text)
- color_table = [f'rgb({r},{g},{b})' for r, g, b in colors]
- while r'\trowd' in below:
- above, below = below.split(r'\trowd', 1)
- title_info = {'text': '', 'color': 'black'}
- if r'\par' in above:
- title_section = above.split(r'\par')[-1]
- color_match = re.search(r'\\cf(\d+)', title_section)
- if color_match and color_table:
- color_idx = int(color_match.group(1))
- if 0 <= color_idx < len(color_table):
- title_info['color'] = color_table[color_idx]
- title_text = re.sub(r'\\[a-z]+\d*', '', title_section)
- title_text = re.sub(r'[{}]', '', title_text)
- title_text = title_text.replace(r'\line', '\n').strip()
- title_info['text'] = title_text
- table_end = below.find(r'\par') if r'\par' in below else len(below)
- tables.append({'title': title_info, 'content': r'\trowd' + below[:table_end]})
- below = below[table_end:]
- return tables
- def parse_rtf(rtf_text):
- if '\\colortbl' in rtf_text:
- colors = [f'rgb({r},{g},{b})' for r, g, b in re.findall(r'\\red(\d+)\\green(\d+)\\blue(\d+)', rtf_text)]
- color_roles = {'\\cf1': 'text','\\cb2': 'background','\\clcbpat3': 'header','\\clcbpat4': 'footer','\\cf4': 'title'}
- for code, role in color_roles.items():
- if code in rtf_text and colors:
- pattern = code.replace('\\', r'\\') + r'(\d+)'
- match = re.search(pattern, rtf_text)
- if match:
- idx = int(match.group(1))
- if idx < len(colors):
- default['styles']['colors'][role] = colors[idx]
- if '\\fonttbl' in rtf_text:
- fonts = re.findall(r'{\\f\d+\\fswiss ([^;]+);}', rtf_text)
- if fonts:
- default['styles']['fonts']['default']['name'] = fonts[0].lower()
- if len(fonts) > 1 and 'bold' in fonts[1].lower():
- default['styles']['fonts']['title']['name'] = fonts[1].lower().replace(' bold', '')
- fs_sizes = [int(size)//2 for size in re.findall(r'\\fs(\d+)', rtf_text)]
- if fs_sizes:
- default['styles']['fonts']['default']['size'] = fs_sizes[0]
- if len(fs_sizes) > 1:
- default['styles']['fonts']['title']['size'] = fs_sizes[1]
- bw_match = re.search(r'\\brdrw(\d+)', rtf_text)
- if bw_match:
- default['styles']['spacing']['line_width'] = max(1, int(bw_match.group(1)))//10
- gap_match = re.search(r'\\trgaph(\d+)', rtf_text)
- if gap_match:
- gap_val = max(1, int(gap_match.group(1))//20)
- default['styles']['spacing'].update({'xpadding': gap_val, 'ypadding': gap_val})
- tables = get_tables(rtf_text)
- for i, row_match in enumerate(re.finditer(r'\\intbl(.*?)\\row', rtf_text, re.DOTALL)):
- row = [clean_rtf_text(cell) for cell in re.findall(r'\\cell\s*(.*?)(?=\\cell|\\row)', row_match.group(1))]
- if i == 0:
- default['headers'] = row
- else:
- default['rows'].append(row)
- return default
- default = {
- 'title': '',
- 'headers': [],
- 'rows': [],
- 'styles': {
- 'colors': {'header': 'yellow','footer': 'lightgreen','title': 'black','outline': 'black','background': 'white','text': 'black'},
- 'fonts': {'default': {'name': 'arial', 'size': 12, 'style': 'regular'},'title': {'name': 'arial', 'size': 16, 'style': 'bold'}},
- 'spacing': {'line_width': 2,'cell_height': 40,'xpadding': 7,'ypadding': 5,'text_spacing': 4},
- 'wrap_chars': 40
- }
- }
- parsed = parse_rtf(rtf_control)
- title_text = parsed['title']
- headers = parsed['headers']
- rows = parsed['rows']
- styles = parsed['styles']
- header_color = styles['colors']['header']
- footer_color = styles['colors']['footer']
- title_color = styles['colors']['title']
- outline_color = styles['colors']['outline']
- bg_color = styles['colors']['background']
- fg_color = styles['colors']['text']
- line_width = styles['spacing']['line_width']
- xpadding = styles['spacing']['xpadding']
- ypadding = styles['spacing']['ypadding']
- text_spacing = styles['spacing']['text_spacing']
- wrap_chars = styles['wrap_chars']
- font = get_font(font_name=styles['fonts']['default']['name'], fontsize=styles['fonts']['default']['size'], style=styles['fonts']['default']['style'])
- title_font = get_font('arial', 14, 'bold')
- def get_wrapped_widths(col_texts):
- max_w = 0
- for text in col_texts:
- wrapped = textwrap.wrap(text, width=wrap_chars)
- for line in wrapped:
- w = font.getsize(line)[0]
- max_w = max(max_w, w)
- return max_w + xpadding * 2
- def set_table(rows, title_text=None):
- line_height = font.getsize('A')[1] + text_spacing
- row_heights = []
- for row in rows:
- max_lines = 0
- for text in row:
- lines = textwrap.wrap(text, width=wrap_chars)
- max_lines = max(max_lines, len(lines))
- row_heights.append(max_lines * line_height + ypadding * 2)
- num_cols = len(headers)
- col_widths = []
- for col in range(num_cols):
- texts = [headers[col]] + [row[col] for row in rows]
- max_width = get_wrapped_widths(texts)
- col_widths.append(max_width)
- img_width = sum(col_widths) + line_width
- img = Image.new('RGB', (img_width+2, 12000), color=bg_color)
- draw = ImageDraw.Draw(img)
- extra_height = 3
- if title_text:
- title_w, title_h = title_font.getsize(title_text)
- extra_height = int(title_h * 1.5) * 2 if '\n' in title_text else int(title_h * 1.5)
- draw.text((3, 3), title_text, fill=title_color, font=title_font)
- x = 1
- for col, text in enumerate(headers):
- width = col_widths[col]
- y0 = extra_height
- draw.rectangle([x, y0, x + width, y0 + line_height + ypadding * 2], fill=header_color, outline=outline_color, width=line_width)
- w, h = draw.textsize(text, font=font)
- draw.text((x + (width - w) // 2, y0 + (line_height + ypadding - h) // 2), text, fill=fg_color, font=font)
- x += width
- for row_idx, row in enumerate(rows):
- y = extra_height + line_height + sum(row_heights[:row_idx]) + ypadding * 2
- x = 1
- for col_idx, text in enumerate(row):
- width = col_widths[col_idx]
- height = row_heights[row_idx]
- fill_color = footer_color if col_idx == 0 else bg_color
- draw.rectangle([x, y, x + width, y + height], fill=fill_color, outline=outline_color, width=line_width)
- lines = textwrap.wrap(text, width=wrap_chars)
- for i, line in enumerate(lines):
- align_x = x + xpadding
- align_y = y + ypadding + i * line_height
- draw.text((align_x, align_y), line, fill=fg_color, font=font)
- x += width
- img_y = y + height + line_width + 2
- img = img.crop((0, 0, img.width, img_y))
- return img
- img = set_table(rows, title_text)
- root = Tk()
- canvas = Canvas(root, width=img.width, height=img.height)
- canvas.pack()
- photo = ImageTk.PhotoImage(img)
- canvas.create_image(line_width, line_width, image=photo, anchor='nw')
- root.mainloop()
Advertisement
Add Comment
Please, Sign In to add comment