here2share

# tk_rtf_tablesheet.py

Sep 3rd, 2025
207
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.35 KB | None | 0 0
  1. # tk_rtf_tablesheet.py
  2.  
  3. from tkinter import Tk, Canvas
  4. from PIL import Image, ImageDraw, ImageTk, ImageFont
  5. import textwrap
  6. import os
  7. import re
  8.  
  9. rtf_control = r'''
  10. {\rtf1\ansi\deff0
  11. {\fonttbl
  12. {\f0\fswiss Arial;}
  13. {\f1\fswiss Arial Bold;}
  14. }
  15.  
  16. {\colortbl;\red0\green0\blue0;\red255\green255\blue255;\red255\green255\blue0;\red0\green255\blue0;}
  17.  
  18. \fs28\b\cf4 RAW Power\line Retail And Wholesale\par
  19. \fs20\b\cf1
  20. {\trowd\trgaph100\trleft-100
  21. \clbrdrb\brdrs\brdrw10\clcbpat3\cellx2000
  22. \clbrdrb\brdrs\brdrw10\clcbpat3\cellx4000
  23. \clbrdrb\brdrs\brdrw10\clcbpat3\cellx6000
  24. \clbrdrb\brdrs\brdrw10\clcbpat3\cellx8000
  25. \pard\intbl Items\cell 1 to 9\cell 10 to 19\cell 20+\cell\row}
  26.  
  27. \fs20\cf1
  28. {\trowd\trgaph100\trleft-100
  29. \clbrdrb\brdrs\brdrw10\clcbpat4\cellx2000
  30. \clbrdrb\brdrs\brdrw10\clcbpat4\cellx4000
  31. \clbrdrb\brdrs\brdrw10\clcbpat4\cellx6000
  32. \clbrdrb\brdrs\brdrw10\clcbpat4\cellx8000
  33. \pard\intbl T-Shirts\cell $10.00\cell $5.00\cell $1.00\cell\row}
  34.  
  35. {\trowd\trgaph100\trleft-100
  36. \clbrdrb\brdrs\brdrw10\clcbpat4\cellx2000
  37. \clbrdrb\brdrs\brdrw10\clcbpat4\cellx4000
  38. \clbrdrb\brdrs\brdrw10\clcbpat4\cellx6000
  39. \clbrdrb\brdrs\brdrw10\clcbpat4\cellx8000
  40. \pard\intbl Jeans\cell $25.00\cell $15.00... and this is to test if the textwrap also works\cell $5.00\cell\row}
  41.  
  42. {\trowd\trgaph100\trleft-100
  43. \clbrdrb\brdrs\brdrw10\clcbpat4\cellx2000
  44. \clbrdrb\brdrs\brdrw10\clcbpat4\cellx4000
  45. \clbrdrb\brdrs\brdrw10\clcbpat4\cellx6000
  46. \clbrdrb\brdrs\brdrw10\clcbpat4\cellx8000
  47. \pard\intbl Jackets\cell $100.00\cell $70.00\cell $40.00\cell\row}
  48. }
  49. '''
  50.  
  51. def get_font(font_name="arial", fontsize=16, style="regular", font_dir="fonts"):
  52.     FONT_STYLES = {"regular": "", "bold": "bd", "italic": "i", "bold_italic": "bi"}
  53.     style = FONT_STYLES.get(style.lower(), "")
  54.     font_file = f"{font_name}{style}.ttf"
  55.     font_path = os.path.join(font_dir, font_file)
  56.     return ImageFont.truetype(font_path, fontsize)
  57.    
  58. def clean_rtf_text(text):
  59.     text = re.sub(r'\\\$', '$', text)
  60.     text = re.sub(r'\\[a-z]+\d*', '', text)
  61.     text = re.sub(r'[{}]', '', text)
  62.     return text.strip()
  63.  
  64. def get_tables(rtf_text):
  65.     below = rtf_text
  66.     tables = []
  67.     color_table = []
  68.     if r'\colortbl' in rtf_text:
  69.         colors = re.findall(r'\\red(\d+)\\green(\d+)\\blue(\d+)', rtf_text)
  70.         color_table = [f'rgb({r},{g},{b})' for r, g, b in colors]
  71.     while r'\trowd' in below:
  72.         above, below = below.split(r'\trowd', 1)
  73.         title_info = {'text': '', 'color': 'black'}
  74.         if r'\par' in above:
  75.             title_section = above.split(r'\par')[-1]
  76.             color_match = re.search(r'\\cf(\d+)', title_section)
  77.             if color_match and color_table:
  78.                 color_idx = int(color_match.group(1))
  79.                 if 0 <= color_idx < len(color_table):
  80.                     title_info['color'] = color_table[color_idx]
  81.             title_text = re.sub(r'\\[a-z]+\d*', '', title_section)
  82.             title_text = re.sub(r'[{}]', '', title_text)
  83.             title_text = title_text.replace(r'\line', '\n').strip()
  84.             title_info['text'] = title_text
  85.         table_end = below.find(r'\par') if r'\par' in below else len(below)
  86.         tables.append({'title': title_info, 'content': r'\trowd' + below[:table_end]})
  87.         below = below[table_end:]
  88.     return tables
  89.  
  90. def parse_rtf(rtf_text):
  91.     if '\\colortbl' in rtf_text:
  92.         colors = [f'rgb({r},{g},{b})' for r, g, b in re.findall(r'\\red(\d+)\\green(\d+)\\blue(\d+)', rtf_text)]
  93.         color_roles = {'\\cf1': 'text','\\cb2': 'background','\\clcbpat3': 'header','\\clcbpat4': 'footer','\\cf4': 'title'}
  94.         for code, role in color_roles.items():
  95.             if code in rtf_text and colors:
  96.                 pattern = code.replace('\\', r'\\') + r'(\d+)'
  97.                 match = re.search(pattern, rtf_text)
  98.                 if match:
  99.                     idx = int(match.group(1))
  100.                     if idx < len(colors):
  101.                         default['styles']['colors'][role] = colors[idx]
  102.     if '\\fonttbl' in rtf_text:
  103.         fonts = re.findall(r'{\\f\d+\\fswiss ([^;]+);}', rtf_text)
  104.         if fonts:
  105.             default['styles']['fonts']['default']['name'] = fonts[0].lower()
  106.             if len(fonts) > 1 and 'bold' in fonts[1].lower():
  107.                 default['styles']['fonts']['title']['name'] = fonts[1].lower().replace(' bold', '')
  108.     fs_sizes = [int(size)//2 for size in re.findall(r'\\fs(\d+)', rtf_text)]
  109.     if fs_sizes:
  110.         default['styles']['fonts']['default']['size'] = fs_sizes[0]
  111.         if len(fs_sizes) > 1:
  112.             default['styles']['fonts']['title']['size'] = fs_sizes[1]
  113.     bw_match = re.search(r'\\brdrw(\d+)', rtf_text)
  114.     if bw_match:
  115.         default['styles']['spacing']['line_width'] = max(1, int(bw_match.group(1)))//10
  116.     gap_match = re.search(r'\\trgaph(\d+)', rtf_text)
  117.     if gap_match:
  118.         gap_val = max(1, int(gap_match.group(1))//20)
  119.         default['styles']['spacing'].update({'xpadding': gap_val, 'ypadding': gap_val})
  120.     tables = get_tables(rtf_text)
  121.     for i, row_match in enumerate(re.finditer(r'\\intbl(.*?)\\row', rtf_text, re.DOTALL)):
  122.         row = [clean_rtf_text(cell) for cell in re.findall(r'\\cell\s*(.*?)(?=\\cell|\\row)', row_match.group(1))]
  123.         if i == 0:
  124.             default['headers'] = row
  125.         else:
  126.             default['rows'].append(row)
  127.     return default
  128.  
  129. default = {
  130.     'title': '',
  131.     'headers': [],
  132.     'rows': [],
  133.     'styles': {
  134.         'colors': {'header': 'yellow','footer': 'lightgreen','title': 'black','outline': 'black','background': 'white','text': 'black'},
  135.         'fonts': {'default': {'name': 'arial', 'size': 12, 'style': 'regular'},'title': {'name': 'arial', 'size': 16, 'style': 'bold'}},
  136.         'spacing': {'line_width': 2,'cell_height': 40,'xpadding': 7,'ypadding': 5,'text_spacing': 4},
  137.         'wrap_chars': 40
  138.     }
  139. }
  140.  
  141. parsed = parse_rtf(rtf_control)
  142.  
  143. title_text = parsed['title']
  144. headers = parsed['headers']
  145. rows = parsed['rows']
  146.  
  147. styles = parsed['styles']
  148. header_color = styles['colors']['header']
  149. footer_color = styles['colors']['footer']
  150. title_color = styles['colors']['title']
  151. outline_color = styles['colors']['outline']
  152. bg_color = styles['colors']['background']
  153. fg_color = styles['colors']['text']
  154. line_width = styles['spacing']['line_width']
  155. xpadding = styles['spacing']['xpadding']
  156. ypadding = styles['spacing']['ypadding']
  157. text_spacing = styles['spacing']['text_spacing']
  158. wrap_chars = styles['wrap_chars']
  159.  
  160. font = get_font(font_name=styles['fonts']['default']['name'], fontsize=styles['fonts']['default']['size'], style=styles['fonts']['default']['style'])
  161. title_font = get_font('arial', 14, 'bold')
  162.  
  163. def get_wrapped_widths(col_texts):
  164.     max_w = 0
  165.     for text in col_texts:
  166.         wrapped = textwrap.wrap(text, width=wrap_chars)
  167.         for line in wrapped:
  168.             w = font.getsize(line)[0]
  169.             max_w = max(max_w, w)
  170.     return max_w + xpadding * 2
  171.  
  172. def set_table(rows, title_text=None):
  173.     line_height = font.getsize('A')[1] + text_spacing
  174.     row_heights = []
  175.     for row in rows:
  176.         max_lines = 0
  177.         for text in row:
  178.             lines = textwrap.wrap(text, width=wrap_chars)
  179.             max_lines = max(max_lines, len(lines))
  180.         row_heights.append(max_lines * line_height + ypadding * 2)
  181.     num_cols = len(headers)
  182.     col_widths = []
  183.     for col in range(num_cols):
  184.         texts = [headers[col]] + [row[col] for row in rows]
  185.         max_width = get_wrapped_widths(texts)
  186.         col_widths.append(max_width)
  187.     img_width = sum(col_widths) + line_width
  188.     img = Image.new('RGB', (img_width+2, 12000), color=bg_color)
  189.     draw = ImageDraw.Draw(img)
  190.     extra_height = 3
  191.     if title_text:
  192.         title_w, title_h = title_font.getsize(title_text)
  193.         extra_height = int(title_h * 1.5) * 2 if '\n' in title_text else int(title_h * 1.5)
  194.         draw.text((3, 3), title_text, fill=title_color, font=title_font)
  195.     x = 1
  196.     for col, text in enumerate(headers):
  197.         width = col_widths[col]
  198.         y0 = extra_height
  199.         draw.rectangle([x, y0, x + width, y0 + line_height + ypadding * 2], fill=header_color, outline=outline_color, width=line_width)
  200.         w, h = draw.textsize(text, font=font)
  201.         draw.text((x + (width - w) // 2, y0 + (line_height + ypadding - h) // 2), text, fill=fg_color, font=font)
  202.         x += width
  203.     for row_idx, row in enumerate(rows):
  204.         y = extra_height + line_height + sum(row_heights[:row_idx]) + ypadding * 2
  205.         x = 1
  206.         for col_idx, text in enumerate(row):
  207.             width = col_widths[col_idx]
  208.             height = row_heights[row_idx]
  209.             fill_color = footer_color if col_idx == 0 else bg_color
  210.             draw.rectangle([x, y, x + width, y + height], fill=fill_color, outline=outline_color, width=line_width)
  211.             lines = textwrap.wrap(text, width=wrap_chars)
  212.             for i, line in enumerate(lines):
  213.                 align_x = x + xpadding
  214.                 align_y = y + ypadding + i * line_height
  215.                 draw.text((align_x, align_y), line, fill=fg_color, font=font)
  216.             x += width
  217.     img_y = y + height + line_width + 2
  218.     img = img.crop((0, 0, img.width, img_y))
  219.     return img
  220.  
  221. img = set_table(rows, title_text)
  222.  
  223. root = Tk()
  224. canvas = Canvas(root, width=img.width, height=img.height)
  225. canvas.pack()
  226. photo = ImageTk.PhotoImage(img)
  227. canvas.create_image(line_width, line_width, image=photo, anchor='nw')
  228. root.mainloop()
  229.  
Advertisement
Add Comment
Please, Sign In to add comment