Package: docx2python-kryon
docx2python - customized for KryonExtract docx headers, footers, text, footnotes, endnotes, properties, and images to a Python object.full documentation of forked projectThe code is an expansion/customization ofdocx2python(Copyright (c) 2019 Shay Hill).shared features:extracts text from docx filesextracts images from docx filesno dependencies (docx2python requires pytest to test)additions:extracts footnotes and endnotesconverts bullets and numbered lists to ascii with indentationretains some structure of the original file (more below)extracts document properties (creator, lastModifiedBy, etc.)inserts image placeholders in text ('----media/image1.jpg----')inserts plain text footnote and endnote references in text ('----footnote1----')(optionally) retains font size, font color, bold, italics, and underscore as htmlfull test coverage and documentation for developerssubtractions:no command-line interfacewill only work with later versions of PythonInstallationpipinstalldocx2python-kryonUsefromdocx2pythonimportdocx2python# extract docx contentdocx2python('path/to/file.docx')# extract docx content, write images to image_directorydocx2python('path/to/file.docx','path/to/image_directory')# extract docx content with basic font styles converted to htmldocx2python('path/to/file.docx',html=True)Note on html feature:font size, font color, bold, italics, and underline supportedevery tag open in a paragraph will be closed in that paragraph (and, where appropriate, reopened in the next paragraph). If two subsequenct paragraphs are bold, they will be returned as<b>paragraph q</b>,<b>paragraph 2</b>. This is intentional to make  each paragraph its own entity.if you specifyexport_font_style=True,>and<in your docx text will be encoded as&gt;and&lt;Return ValueFunctiondocx2pythonreturns an object with several attributes.header- contents of the docx headers in the return format described hereinfooter- contents of the docx footers in the return format described hereinbody- contents of the docx in the return format described hereinfootnotes- contents of the docx in the return format described hereinendnotes- contents of the docx in the return format described hereindocument- header  + body + footer (read only)text- all docx text as one string, similar to what you'd get frompython-docx2txtproperties- docx property names mapped to values (e.g.,{"lastModifiedBy": "Shay Hill"})images- image names mapped to images in binary format. Write to filesystem withfor name, image in result.images.items():
    with open(name, 'wb') as image_destination:
        write(image_destination, image)Return FormatSome structure will be maintained. Text will be returned in a nested list, with paragraphs always at depth 4 (i.e.,output.body[i][j][k][l]will be a paragraph).If your docx has no tables, output.body will appear as one a table with all contents in one cell:[# document[# table[# row[# cell"Paragraph 1","Paragraph 2","- bulleted list","- continuing bulleted list","1)  numbered list","2)  continuing numbered list""    a)  sublist","        i)  sublist of sublist","3)  keeps track of indention levels","    a)  resets sublist counters"]]]]Table cells will appear as table cells. Text outside tables will appear as table cells.To preserve the even depth (text always at depth 4), nested tables will appear as new, top-level tables. This is clearer with an example:#  docx structure[# document[# table A[# table A row[# table A cell 1"paragraph in table A cell 1"],[# nested table B[# table B row[# table B cell"paragraph in table B"]]],[# table A cell 2'paragraph in table A cell 2']]]]becomes ...[# document[# table A[# row in table A[# cell in table A"table A cell 1"]]],[# table B[# row in table B[# cell in table B"table B cell"]]],[# table C[# row in table C[# cell in table C"table A cell 2"]]]]This ensures text appearsonly oncein the order it appears on the docxalways at depth four (i.e.,result.body[i][j][k][l]will be a string).Working with outputThis package provides several documented helper functions inthedocx2python.iteratorsmodule. Here are a few recipes possible with these functions:fromdocx2python.iteratorsimportenum_cellsdefremove_empty_paragraphs(tables):for(i,j,k),cellinenum_cells(tables):tables[i][j][k]=[xforxincellifx]>>> tables = [[[['a', 'b'], ['a', '', 'd', '']]]]
>>> remove_empty_paragraphs(tables)
    [[[['a', 'b'], ['a', 'd']]]]fromdocx2python.iteratorsimportenum_at_depthdefhtml_map(tables)->str:"""Create an HTML map of document contents.Render this in a browser to visually search for data."""tables=self.document# prepend index tuple to each paragraphfor(i,j,k,l),paragraphinenum_at_depth(tables,4):tables[i][j][k][l]=" ".join([str((i,j,k,l)),paragraph])# wrap each paragraph in <pre> tagsfor(i,j,k),cellinenum_at_depth(tables,3):tables[i][j][k]="".join([f"<pre>{x}</pre>"forxincell])# wrap each cell in <td> tagsfor(i,j),rowinenum_at_depth(tables,2):tables[i][j]="".join([f"<td>{x}</td>"forxinrow])# wrap each row in <tr> tagsfor(i,),tableinenum_at_depth(tables,1):tables[i]="".join(f"<tr>{x}</tr>"forxintable)# wrap each table in <table> tagstables="".join([f'<table border="1">{x}</table>'forxintables])return["<html><body>"]+tables+["</body></html>"]>>> tables = [[[['a', 'b'], ['a', 'd']]]]
>>> html_toc(tables)
<html>
    <body>
        <table border="1">
            <tr>
                <td>
                    '(0, 0, 0, 0) a'
                    '(0, 0, 0, 1) b'
                </td>
                <td>
                    '(0, 0, 1, 0) a'
                    '(0, 0, 1, 1) d'
                </td>
            </tr>
        </table>
    </body>
</html>See helper functions.