import os, sys
import pandas as pd


import collections
import collections.abc
# the above packages are required for importing pptx, on Python 3.10.X+

from pptx import Presentation
from pptx.util import Inches, Pt, Cm
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_AUTO_SIZE, MSO_ANCHOR
from pptx.oxml.xmlchemy import OxmlElement

import xml.etree.ElementTree as ET

class PresentationFeature():

    # new textbox and add text
    @staticmethod
    def add_textbox(slide, text, left=0, top=0, width=0, height=0, *, size=Pt(12)):

        txBox = slide.shapes.add_textbox(Inches(left), Inches(top), width, height)
        text_frame = txBox.text_frame
        text_frame.word_wrap = True         # for libreoffic, if not having this line, the textbox will go off the slide

        p = text_frame.add_paragraph()
        p.text = text
        p.font.size = size

        return txBox

    # set text alignment inside textbox
    @staticmethod
    def set_textbox_alignment(textbox, horizen_type):
        for paragraph in textbox.text_frame.paragraphs:
            paragraph.alignment = horizen_type      # set horizen alignment

    # add text to a table's cell
    @staticmethod
    def add_text_to_cell(cell, str_text):
        paragraph = cell.text_frame.paragraphs[-1]
        run = paragraph.add_run()
        run.text = str_text

    # add text to a cell's run and new lines by the length of string_len
    @staticmethod
    def add_text_with_newlines(cell, list_ctry, *, string_len):
        str_ctry = ""
        for i in range(len(list_ctry)):
            if i > 0:
                str_ctry += ","
            str_ctry += list_ctry[i]

            if len(str_ctry) >= string_len:
                paragraph = cell.text_frame.paragraphs[-1]
                run = paragraph.add_run()
                run.text = str_ctry + "\n"
                str_ctry = ""

    # setting all table cell's text size
    @staticmethod
    def set_table_text_size(table, *, size):
        # setting font size
        for col in range(len(table.columns)):
            for row in range(len(table.rows)):
                for cell_pt in table.cell(row, col).text_frame.paragraphs:
                    cell_pt.font.size = size

    # setting single cell's text size
    @staticmethod
    def set_cell_text_size(cell, *, size):
        font = cell.font
        font.size = size

    # set all cell's text color
    @staticmethod
    def set_table_text_color(table, RGBcolor):
        for col in range(len(table.columns)):
            for row in range(len(table.rows)):
                for cell_pt in table.cell(row, col).text_frame.paragraphs:
                    cell_pt.font.color.rgb = RGBcolor

    # new a table without formating or styling
    @staticmethod
    def add_table(slide, row = 0, col = 0, left = 0, top = 0):
        shape = slide.shapes

        left = Inches(left)
        top = Inches(top)
        width = height = Inches(1)

        table = shape.add_table(row, col, left, top, width, height).table
        return table

    # construct table by given dataframe
    @staticmethod
    def add_table_by_df(slide, df, left = 0, top = 0):
        shape = slide.shapes

        left = Inches(left)
        top = Inches(top)
        width = height = Inches(1)

        row = df.shape[0]
        col = df.shape[1]

        table = shape.add_table(row, col, left, top, width, height).table
        for col in range(len(table.columns)):
            for row in range(len(table.rows)):
                table.cell(row, col).text = df.iloc[row, col]
        return table

    # format the column width and text size
    @staticmethod
    def resize_table(table, font_size):
        # setting font size
        for col in range(len(table.columns)):
            for row in range(len(table.rows)):
                for cell_pt in table.cell(row, col).text_frame.paragraphs:
                    cell_pt.font.size = font_size
        # format the column by finding the max length run in paragraphs
        list_col_max_width = [0 for x in range(len(table.columns))]
        for col in range(len(table.columns)):
            for row in range(len(table.rows)):
                for paragraphs in table.cell(row, col).text_frame.paragraphs:
                    for run in paragraphs.runs:
                        list_col_max_width[col] = max(list_col_max_width[col], len(run.text)*(font_size))
        # setting column width
        for col in range(len(table.columns)):
            table.columns[col].width = list_col_max_width[col] + Cm(0.25)

    # set multiple column width with corresponding given value
    @staticmethod
    def set_column_width(table, list_col_idx, list_width):
        for col in list_col_idx:
            table.columns[col].width = list_width[col]


    # set cell text alignment by table
    @staticmethod
    def set_table_alignment(table, horizen_type, vertical_type):
        for row in range(len(table.rows)):
            for col in range(len(table.columns)):
                table.cell(row, col).vertical_anchor = vertical_type    # set vertical alignment
                for paragraph in table.cell(row, col).text_frame.paragraphs:
                    paragraph.alignment = horizen_type      # set horizen alignment

    # fill cell background
    @staticmethod
    def set_cell_fill(table, list_cell_coord, RGBcolor):
        for row, col in list_cell_coord:
            cell = table.cell(row, col)
            fill = cell.fill
            fill.solid()
            fill.fore_color.rgb = RGBcolor

    # fill table(all-cell) background
    @staticmethod
    def set_table_fill(table, RGBcolor):
        for row in range(len(table.rows)):
            for col in range(len(table.columns)):
                cell = table.cell(row, col)
                fill = cell.fill
                fill.solid()
                fill.fore_color.rgb = RGBcolor

    # print table value
    @staticmethod
    def print_table(table):
        for row in range(len(table.rows)):
            for col in range(len(table.columns)):
                print(table.cell(row, col).text_frame.text, end=" ")
            print()

    # new xml element to set style
    @staticmethod
    def SubElement(parent, tagname, **kwargs):
        element = OxmlElement(tagname)
        element.attrib.update(kwargs)
        parent.append(element)
        return element


    # set border style by modifying xml
    @classmethod
    def set_table_border(cls, table, border_color="444444", border_width='12700'):
        for row in range(len(table.rows)):
            for col in range(len(table.columns)):
                cell = table.cell(row, col)
                tc = cell._tc                   # <class 'pptx.oxml.table.CT_TableCell'> as a xml element
                tcPr = tc.get_or_add_tcPr()     # <class 'pptx.oxml.table.CT_TableCellProperties'> as a xml element
                for lines in ['a:lnL','a:lnR','a:lnT','a:lnB']:
                    ln = cls.SubElement(tcPr, lines, w=border_width, cap='flat', cmpd='sng', algn='ctr')
                    solidFill = cls.SubElement(ln, 'a:solidFill')
                    srgbClr = cls.SubElement(solidFill, 'a:srgbClr', val=border_color)
                    prstDash = cls.SubElement(ln, 'a:prstDash', val='solid')
                    round_ = cls.SubElement(ln, 'a:round')
                    headEnd = cls.SubElement(ln, 'a:headEnd', type='none', w='med', len='med')
                    tailEnd = cls.SubElement(ln, 'a:tailEnd', type='none', w='med', len='med')


    # set dblstrike on run tag by xml
    @classmethod
    def set_dblstrike(cls, table, list_run_text):
        for row in range(len(table.rows)):
            for col in range(len(table.columns)):
                for paragraphs in table.cell(row, col).text_frame.paragraphs:
                    for run in paragraphs.runs:
                        if run.text in list_run_text:
                            r = run._r
                            rPr = cls.SubElement(r, 'a:rPr', strike="dblStrike")

    # find the rPr tag with dblStrike attrib under run, then return a list of quilfied run-text
    @staticmethod
    def find_dblstrike(table):
        namespaces = {'a': 'http://schemas.openxmlformats.org/drawingml/2006/main'}

        list_run_text = []
        for row in range(len(table.rows)):
            for col in range(len(table.columns)):
                cell = table.cell(row, col)
                tc = cell._tc
                list_rPr = tc.findall('.//a:rPr[@strike="dblStrike"]', namespaces)
                for rPr in list_rPr:
                    r = rPr.find('..')
                    t = r.find('.//a:t', namespaces)
                    list_run_text.append(t.text)
        return list_run_text

    # set table color that the srchClr val is not "000000"
    @classmethod
    def set_color(cls, table, dict_run_text):
        for row in range(len(table.rows)):
            for col in range(len(table.columns)):
                for paragraphs in table.cell(row, col).text_frame.paragraphs:
                    for run in paragraphs.runs:
                        if run.text in dict_run_text.keys():
                            r = run._r
                            rPr = r.get_or_add_rPr()
                            solidFill = cls.SubElement(rPr, 'a:solidFill')
                            srgbClr = cls.SubElement(solidFill, 'a:srgbClr', val=dict_run_text[run.text])

    # find the run's color that is not "000000" and return that color and text as dict
    @staticmethod
    def find_color(table):
        namespaces = {'a': 'http://schemas.openxmlformats.org/drawingml/2006/main'}

        dict_run_text = {}
        for row in range(len(table.rows)):
            for col in range(len(table.columns)):
                for paragraphs in table.cell(row, col).text_frame.paragraphs:
                    for run in paragraphs.runs:
                        r = run._r
                        srgbClr = r.find('.//a:srgbClr', namespaces)
                        if srgbClr.attrib["val"] != "000000":
                            t = r.find('.//a:t', namespaces)
                            dict_run_text[t.text] = srgbClr.attrib["val"]
        return dict_run_text

    # copy table value by runs
    @staticmethod
    def copy_table_value(dst_table, src_table):
        for row in range(len(src_table.rows)):
            for col in range(len(src_table.columns)):
                for paragraph in src_table.cell(row, col).text_frame.paragraphs:
                    dst_p = dst_table.cell(row, col).text_frame.add_paragraph()
                    for run in paragraph.runs:
                        dst_run = dst_p.add_run()
                        dst_run.text = run.text

    # output a table's xml file
    @classmethod
    def print_table_xml(cls, table, table_name, path=os.getcwd()):
        with open(table_name + "_table.xml", "w") as f:
            f.write(str(table._tbl.xml))

import os, sys
import pandas as pd


import collections
import collections.abc
# the above packages are required for importing pptx, on Python 3.10.X+

from pptx import Presentation
from pptx.util import Inches, Pt, Cm
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_AUTO_SIZE, MSO_ANCHOR
from pptx.oxml.xmlchemy import OxmlElement

import xml.etree.ElementTree as ET

class PresentationFeature():

    # new textbox and add text
    @staticmethod
    def add_textbox(slide, text, left=0, top=0, width=0, height=0, *, size=Pt(12)):

        txBox = slide.shapes.add_textbox(Inches(left), Inches(top), width, height)
        text_frame = txBox.text_frame
        text_frame.word_wrap = True         # for libreoffic, if not having this line, the textbox will go off the slide

        p = text_frame.add_paragraph()
        p.text = text
        p.font.size = size

        return txBox

    # set text alignment inside textbox
    @staticmethod
    def set_textbox_alignment(textbox, horizen_type):
        for paragraph in textbox.text_frame.paragraphs:
            paragraph.alignment = horizen_type      # set horizen alignment

    # add text to a table's cell
    @staticmethod
    def add_text_to_cell(cell, str_text):
        paragraph = cell.text_frame.paragraphs[-1]
        run = paragraph.add_run()
        run.text = str_text

    # add text to a cell's run and new lines by the length of string_len
    @staticmethod
    def add_text_with_newlines(cell, list_ctry, *, string_len):
        str_ctry = ""
        for i in range(len(list_ctry)):
            if i > 0:
                str_ctry += ","
            str_ctry += list_ctry[i]

            if len(str_ctry) >= string_len:
                paragraph = cell.text_frame.paragraphs[-1]
                run = paragraph.add_run()
                run.text = str_ctry + "\n"
                str_ctry = ""

    # setting all table cell's text size
    @staticmethod
    def set_table_text_size(table, *, size):
        # setting font size
        for col in range(len(table.columns)):
            for row in range(len(table.rows)):
                for cell_pt in table.cell(row, col).text_frame.paragraphs:
                    cell_pt.font.size = size

    # setting single cell's text size
    @staticmethod
    def set_cell_text_size(cell, *, size):
        font = cell.font
        font.size = size

    # set all cell's text color
    @staticmethod
    def set_table_text_color(table, RGBcolor):
        for col in range(len(table.columns)):
            for row in range(len(table.rows)):
                for cell_pt in table.cell(row, col).text_frame.paragraphs:
                    cell_pt.font.color.rgb = RGBcolor

    # new a table without formating or styling
    @staticmethod
    def add_table(slide, row = 0, col = 0, left = 0, top = 0):
        shape = slide.shapes

        left = Inches(left)
        top = Inches(top)
        width = height = Inches(1)

        table = shape.add_table(row, col, left, top, width, height).table
        return table

    # construct table by given dataframe
    @staticmethod
    def add_table_by_df(slide, df, left = 0, top = 0):
        shape = slide.shapes

        left = Inches(left)
        top = Inches(top)
        width = height = Inches(1)

        row = df.shape[0]
        col = df.shape[1]

        table = shape.add_table(row, col, left, top, width, height).table
        for col in range(len(table.columns)):
            for row in range(len(table.rows)):
                table.cell(row, col).text = df.iloc[row, col]
        return table

    # format the column width and text size
    @staticmethod
    def resize_table(table, font_size):
        # setting font size
        for col in range(len(table.columns)):
            for row in range(len(table.rows)):
                for cell_pt in table.cell(row, col).text_frame.paragraphs:
                    cell_pt.font.size = font_size
        # format the column by finding the max length run in paragraphs
        list_col_max_width = [0 for x in range(len(table.columns))]
        for col in range(len(table.columns)):
            for row in range(len(table.rows)):
                for paragraphs in table.cell(row, col).text_frame.paragraphs:
                    for run in paragraphs.runs:
                        list_col_max_width[col] = max(list_col_max_width[col], len(run.text)*(font_size))
        # setting column width
        for col in range(len(table.columns)):
            table.columns[col].width = list_col_max_width[col] + Cm(0.25)

    # set multiple column width with corresponding given value
    @staticmethod
    def set_column_width(table, list_col_idx, list_width):
        for col in list_col_idx:
            table.columns[col].width = list_width[col]


    # set cell text alignment by table
    @staticmethod
    def set_table_alignment(table, horizen_type, vertical_type):
        for row in range(len(table.rows)):
            for col in range(len(table.columns)):
                table.cell(row, col).vertical_anchor = vertical_type    # set vertical alignment
                for paragraph in table.cell(row, col).text_frame.paragraphs:
                    paragraph.alignment = horizen_type      # set horizen alignment

    # fill cell background
    @staticmethod
    def set_cell_fill(table, list_cell_coord, RGBcolor):
        for row, col in list_cell_coord:
            cell = table.cell(row, col)
            fill = cell.fill
            fill.solid()
            fill.fore_color.rgb = RGBcolor

    # fill table(all-cell) background
    @staticmethod
    def set_table_fill(table, RGBcolor):
        for row in range(len(table.rows)):
            for col in range(len(table.columns)):
                cell = table.cell(row, col)
                fill = cell.fill
                fill.solid()
                fill.fore_color.rgb = RGBcolor

    # print table value
    @staticmethod
    def print_table(table):
        for row in range(len(table.rows)):
            for col in range(len(table.columns)):
                print(table.cell(row, col).text_frame.text, end=" ")
            print()

    # new xml element to set style
    @staticmethod
    def SubElement(parent, tagname, **kwargs):
        element = OxmlElement(tagname)
        element.attrib.update(kwargs)
        parent.append(element)
        return element


    # set border style by modifying xml
    @classmethod
    def set_table_border(cls, table, border_color="444444", border_width='12700'):
        for row in range(len(table.rows)):
            for col in range(len(table.columns)):
                cell = table.cell(row, col)
                tc = cell._tc                   # <class 'pptx.oxml.table.CT_TableCell'> as a xml element
                tcPr = tc.get_or_add_tcPr()     # <class 'pptx.oxml.table.CT_TableCellProperties'> as a xml element
                for lines in ['a:lnL','a:lnR','a:lnT','a:lnB']:
                    ln = cls.SubElement(tcPr, lines, w=border_width, cap='flat', cmpd='sng', algn='ctr')
                    solidFill = cls.SubElement(ln, 'a:solidFill')
                    srgbClr = cls.SubElement(solidFill, 'a:srgbClr', val=border_color)
                    prstDash = cls.SubElement(ln, 'a:prstDash', val='solid')
                    round_ = cls.SubElement(ln, 'a:round')
                    headEnd = cls.SubElement(ln, 'a:headEnd', type='none', w='med', len='med')
                    tailEnd = cls.SubElement(ln, 'a:tailEnd', type='none', w='med', len='med')


    # set dblstrike on run tag by xml
    @classmethod
    def set_dblstrike(cls, table, list_run_text):
        for row in range(len(table.rows)):
            for col in range(len(table.columns)):
                for paragraphs in table.cell(row, col).text_frame.paragraphs:
                    for run in paragraphs.runs:
                        if run.text in list_run_text:
                            r = run._r
                            rPr = cls.SubElement(r, 'a:rPr', strike="dblStrike")

    # find the rPr tag with dblStrike attrib under run, then return a list of quilfied run-text
    @staticmethod
    def find_dblstrike(table):
        namespaces = {'a': 'http://schemas.openxmlformats.org/drawingml/2006/main'}

        list_run_text = []
        for row in range(len(table.rows)):
            for col in range(len(table.columns)):
                cell = table.cell(row, col)
                tc = cell._tc
                list_rPr = tc.findall('.//a:rPr[@strike="dblStrike"]', namespaces)
                for rPr in list_rPr:
                    r = rPr.find('..')
                    t = r.find('.//a:t', namespaces)
                    list_run_text.append(t.text)
        return list_run_text

    # set table color that the srchClr val is not "000000"
    @classmethod
    def set_color(cls, table, dict_run_text):
        for row in range(len(table.rows)):
            for col in range(len(table.columns)):
                for paragraphs in table.cell(row, col).text_frame.paragraphs:
                    for run in paragraphs.runs:
                        if run.text in dict_run_text.keys():
                            r = run._r
                            rPr = r.get_or_add_rPr()
                            solidFill = cls.SubElement(rPr, 'a:solidFill')
                            srgbClr = cls.SubElement(solidFill, 'a:srgbClr', val=dict_run_text[run.text])

    # find the run's color that is not "000000" and return that color and text as dict
    @staticmethod
    def find_color(table):
        namespaces = {'a': 'http://schemas.openxmlformats.org/drawingml/2006/main'}

        dict_run_text = {}
        for row in range(len(table.rows)):
            for col in range(len(table.columns)):
                for paragraphs in table.cell(row, col).text_frame.paragraphs:
                    for run in paragraphs.runs:
                        r = run._r
                        srgbClr = r.find('.//a:srgbClr', namespaces)
                        if srgbClr.attrib["val"] != "000000":
                            t = r.find('.//a:t', namespaces)
                            dict_run_text[t.text] = srgbClr.attrib["val"]
        return dict_run_text

    # copy table value by runs
    @staticmethod
    def copy_table_value(dst_table, src_table):
        for row in range(len(src_table.rows)):
            for col in range(len(src_table.columns)):
                for paragraph in src_table.cell(row, col).text_frame.paragraphs:
                    dst_p = dst_table.cell(row, col).text_frame.add_paragraph()
                    for run in paragraph.runs:
                        dst_run = dst_p.add_run()
                        dst_run.text = run.text

    # output a table's xml file
    @classmethod
    def print_table_xml(cls, table, table_name, path=os.getcwd()):
        with open(table_name + "_table.xml", "w") as f:
            f.write(str(table._tbl.xml))

# the above packages are required for importing pptx, on Python 3.10.X+ # new textbox and add text # for libreoffic, if not having this line, the textbox will go off the slide # set text alignment inside textbox # set horizen alignment # add text to a table's cell # add text to a cell's run and new lines by the length of string_len # setting all table cell's text size # setting font size # setting single cell's text size # set all cell's text color # new a table without formating or styling # construct table by given dataframe # format the column width and text size # setting font size # format the column by finding the max length run in paragraphs # setting column width # set multiple column width with corresponding given value # set cell text alignment by table # set vertical alignment # set horizen alignment # fill cell background # fill table(all-cell) background # print table value # new xml element to set style # set border style by modifying xml # <class 'pptx.oxml.table.CT_TableCell'> as a xml element # <class 'pptx.oxml.table.CT_TableCellProperties'> as a xml element # set dblstrike on run tag by xml # find the rPr tag with dblStrike attrib under run, then return a list of quilfied run-text # set table color that the srchClr val is not "000000" # find the run's color that is not "000000" and return that color and text as dict # copy table value by runs # output a table's xml file