Files
sionrui/.claude/skills/pptx/scripts/__pycache__/inventory.cpython-312.pyc

312 lines
40 KiB
Plaintext
Raw Normal View History

2026-01-27 00:39:12 +08:00
<EFBFBD>
a<>Oi<4F><69><00> <00>z<00>dZddlZddlZddlZddlZddlmZddlmZddl m
Z
m Z m Z m Z mZmZddlmZmZmZddlmZddlmZdd lmZeeeeedfZe eefZe eeeeee ee ee ee
fdffZ e ee ed
ffZ!e ee ee ffZ"d <0B>Z#eGd <0C>d <0A><00>Z$Gd<0E>d<0F>Z%Gd<10>d
<EFBFBD>Z&dedefd<13>Z' d'dededede e$fd<16>Z(de e&de e&fd<18>Z) d(deeeeefdeeeeefdedeeeffd<1C>Z*de e&ddfd<1D>Z+ d)dede e
d ede!fd!<21>Z,d*ded ede"fd"<22>Z-d#e!d$eddfd%<25>Z.e/d&k(re#<23>yy)+a<>
Extract structured text content from PowerPoint presentations.
This module provides functionality to:
- Extract all text content from PowerPoint shapes
- Preserve paragraph formatting (alignment, bullets, fonts, spacing)
- Handle nested GroupShapes recursively with correct absolute positions
- Sort shapes by visual position on slides
- Filter out slide numbers and non-content placeholders
- Export to JSON with clean, structured data
Classes:
ParagraphData: Represents a text paragraph with formatting
ShapeData: Represents a shape with position and text content
Main Functions:
extract_text_inventory: Extract all text from a presentation
save_inventory: Save extracted data to JSON
Usage:
python inventory.py input.pptx output.json
<EFBFBD>N)<01> dataclass<73><01>Path)<06>Any<6E>Dict<63>List<73>Optional<61>Tuple<6C>Union)<03>Image<67> ImageDraw<61> ImageFont)<01> Presentation)<01>PP_ALIGN)<01> BaseShape<70> ShapeDatac<00><><00>tjdtjd<02><03>}|jdd<05><06>|jdd<08><06>|jd d
d <0B> <0C>|j <00>}t |j <00>}|j<00>s-td |j <00><00><02>tjd<0E>|jj<00>dk(s td<10>tjd<0E> td|j <00><00><02>|jr td<12>t||j<00><13>}t |j<00>}|j j#dd<14><15>t%||<04>td|j<00><00><02>t'|<03>}t)d<17>|j+<00>D<00><00>}|jr$|dkDrtd|<06>d|<05>d<1B><05>y!td<1C>y!td|<05>d|<06>d<1F><05>y!#t,$rA}td |<07><00><02>dd!l}|j1<00>tjd<0E>Yd!}~y!d!}~wwxYw)"z(Main entry point for command-line usage.zFExtract text inventory from PowerPoint with proper GroupShape support.a(
Examples:
python inventory.py presentation.pptx inventory.json
Extracts text inventory with correct absolute positions for grouped shapes
python inventory.py presentation.pptx inventory.json --issues-only
Extracts only text shapes that have overflow or overlap issues
The output JSON includes:
- All text content organized by slide and shape
- Correct absolute positions for shapes in groups
- Visual position and size in inches
- Paragraph properties and formatting
- Issue detection: text overflow and shape overlaps
)<03> description<6F>formatter_class<73>epilog<6F>inputzInput PowerPoint file (.pptx))<01>help<6C>outputzOutput JSON file for inventoryz --issues-only<6C>
store_truez=Include only text shapes that have overflow or overlap issues)<02>actionrzError: Input file not found: <20>z.pptxz.Error: Input must be a PowerPoint file (.pptx)z Extracting text inventory from: zDFiltering to include only text shapes with issues (overflow/overlap)<29><01> issues_onlyT)<02>parents<74>exist_okzOutput saved to: c3<00>2K<00>|]}t|<01><00><01><00>y<00>w<01>N)<01>len)<02>.0<EFBFBD>shapess <20><d:\projects\sionrui\.claude\skills\pptx\scripts\inventory.py<70> <genexpr>zmain.<locals>.<genexpr>ks<00><00><><00>H<>5G<35>6<EFBFBD>3<EFBFBD>v<EFBFBD>;<3B>5G<35>s<00>rzFound z text elements with issues in z slideszNo issues discoveredzFound text in z slides with z text elementszError processing presentation: N)<19>argparse<73>ArgumentParser<65>RawDescriptionHelpFormatter<65> add_argument<6E>
parse_argsrr<00>exists<74>print<6E>sys<79>exit<69>suffix<69>lowerr<00>extract_text_inventoryr<00>parent<6E>mkdir<69>save_inventoryr#<00>sum<75>values<65> Exception<6F> traceback<63> print_exc) <09>parser<65>args<67>
input_path<EFBFBD> inventory<72> output_path<74> total_slides<65> total_shapes<65>er:s r&<00>mainrD2s<00><00> <15> $<24> $<24>\<5C> <20><<3C><<3C> <0C><06>F<EFBFBD>( <0B><17><17><07>&E<><17>F<>
<EFBFBD><17><17><08>'G<><17>H<>
<EFBFBD><17><17><17><1B> L<><18><06> <12> <1C> <1C> <1E>D<EFBFBD><15>d<EFBFBD>j<EFBFBD>j<EFBFBD>!<21>J<EFBFBD> <15> <1C> <1C> <1E> <0A>-<2D>d<EFBFBD>j<EFBFBD>j<EFBFBD>\<5C>:<3A>;<3B> <0B><08><08><11> <0B> <15> <1C> <1C> "<22> "<22> $<24><07> /<2F> <0A>><3E>?<3F> <0B><08><08><11> <0B>"<14> <0A>0<><14><1A><1A> <0C>=<3D>><3E> <0F> <1B> <1B> <11>V<> <0E>+<2B>:<3A>4<EFBFBD>CS<43>CS<43>T<> <09><1A>4<EFBFBD>;<3B>;<3B>'<27> <0B><13><1A><1A> <20> <20><14><04> <20>=<3D><16>y<EFBFBD>+<2B>.<2E> <0A>!<21>$<24>+<2B>+<2B><1D>/<2F>0<><1B>9<EFBFBD>~<7E> <0C><1A>H<>Y<EFBFBD>5E<35>5E<35>5G<35>H<>H<> <0C> <0F> <1B> <1B><1B>a<EFBFBD><1F><15><1C>\<5C>N<EFBFBD>*H<><1C><0E>V]<5D>^<5E><12><16>,<2C>-<2D> <11> <20><1C><0E>m<EFBFBD>L<EFBFBD>><3E><1E>X<> <0E><> <15><14> <0A>/<2F><01>s<EFBFBD>3<>4<><18><11><1B><1B><1D> <0B><08><08><11> <0B> <0B><> <14>s%<00>C*H <00>, H <00>8H <00> I<03>7I<03>Ic<00>0<00>eZdZUdZeed<eed<eed<y)<06>ShapeWithPositionz0A shape with its absolute position on the slide.<2E>shape<70> absolute_left<66> absolute_topN)<07>__name__<5F>
__module__<EFBFBD> __qualname__<5F>__doc__r<00>__annotations__<5F>int<6E><00>r&rFrF<00>s<00><00>:<3A> <14><14><16><16><15>rQrFc<00>(<00>eZdZdZdefd<03>Zdefd<05>Zy)<07> ParagraphDatazNData structure for paragraph properties extracted from a PowerPoint paragraph.<2E> paragraphc<00><><00>|jj<00>|_d|_d|_d|_d|_d|_d|_d|_d|_ d|_
d|_ d|_ d|_ d|_t|d<03>r<>|j <00>z|j j"<00>d|j j"}d}|j%|<03>d<05><02><00>|j%|<03>d<06><02><00>$d|_t|d<08>r|j|_t|d <09>r`|j<00>Tt&j(d
t&j*d t&j,d i}|j|vr||j|_t|d <0A>r'|j
r|j
j.|_t|d<0E>r'|j r|j j.|_|j0r<>|j0d}t|d<10>r<>|j2}|j4r|j4|_|j6r|j6j.|_|j<00>|j|_ |j<00>|j|_
|j<00>|j|_ |jj8r$t;|jj8<00>|_ t|d<11>r<>|j<00>ut|jd<12>r&tA|jj.d<13>|_y|jr |jnd}tA|j|zd<13>|_yyy#t<t>f$rU |jjr%|jjj4|_ n#t<t>f$rYnwxYwY<00><>wxYw)z}Initialize from a PowerPoint paragraph object.
Args:
paragraph: The PowerPoint paragraph object
FN<46>_pz7{http://schemas.openxmlformats.org/drawingml/2006/main}<7D>buChar<61> buAutoNumT<6D>level<65> alignment<6E>CENTER<45>RIGHT<48>JUSTIFY<46> space_before<72> space_afterr<00>font<6E> line_spacing<6E>pt<70>g(@)!<21>text<78>strip<69>bulletrYrZr^r_<00> font_name<6D> font_size<7A>bold<6C>italic<69> underline<6E>color<6F> theme_colorra<00>hasattrrV<00>pPr<50>findrr[r\r]rb<00>runsr`<00>name<6D>size<7A>rgb<67>str<74>AttributeError<6F> TypeError<6F>round)<08>selfrTro<00>ns<6E> alignment_map<61> first_runr`rhs r&<00>__init__zParagraphData.__init__<5F>s'<00><00> #<23><1E><1E>-<2D>-<2D>/<2F><04> <09>!<21><04> <0B>$(<28><04>
<EFBFBD>(,<2C><04><0E>-1<><04><19>,0<><04><18>(,<2C><04><0E>*.<2E><04><0E>$(<28><04> <09>&*<2A><04> <0B>)-<2D><04><0E>$(<28><04>
<EFBFBD>*.<2E><04><18>-1<><04><19> <14>I<EFBFBD>t<EFBFBD> $<24><19> <0C> <0C>(<28><19> <0C> <0C> <20> <20>,<2C><1B>,<2C>,<2C>"<22>"<22>C<EFBFBD>J<>B<EFBFBD><13><08><08>B<EFBFBD>4<EFBFBD>v<EFBFBD><1D>'<27>3<><16>8<EFBFBD>8<EFBFBD>r<EFBFBD>d<EFBFBD>)<29>,<2C>-<2D>9<>"<22><04> <0B><1A>9<EFBFBD>g<EFBFBD>.<2E>!*<2A><1F><1F>D<EFBFBD>J<EFBFBD> <13>9<EFBFBD>k<EFBFBD> *<2A>y<EFBFBD>/B<>/B<>/N<><18><0F><0F><18><18><0E><0E><07><18> <20> <20>)<29><0E>M<EFBFBD>
<19>"<22>"<22>m<EFBFBD>3<>!.<2E>y<EFBFBD>/B<>/B<>!C<><04><0E> <13>9<EFBFBD>n<EFBFBD> -<2D>)<29>2H<32>2H<32> )<29> 6<> 6<> 9<> 9<>D<EFBFBD> <1D> <12>9<EFBFBD>m<EFBFBD> ,<2C><19>1F<31>1F<31>(<28>4<>4<>7<>7<>D<EFBFBD> <1C> <15>><3E>><3E>!<21><0E><0E>q<EFBFBD>)<29>I<EFBFBD><16>y<EFBFBD>&<26>)<29> <20>~<7E>~<7E><04><17>9<EFBFBD>9<EFBFBD>%)<29>Y<EFBFBD>Y<EFBFBD>D<EFBFBD>N<EFBFBD><17>9<EFBFBD>9<EFBFBD>%)<29>Y<EFBFBD>Y<EFBFBD>\<5C>\<5C>D<EFBFBD>N<EFBFBD><17>9<EFBFBD>9<EFBFBD>(<28> $<24> <09> <09>D<EFBFBD>I<EFBFBD><17>;<3B>;<3B>*<2A>"&<26>+<2B>+<2B>D<EFBFBD>K<EFBFBD><17>><3E>><3E>-<2D>%)<29>^<5E>^<5E>D<EFBFBD>N<EFBFBD>
<1D><1B>z<EFBFBD>z<EFBFBD>~<7E>~<7E>%(<28><14><1A><1A><1E><1E>%8<><04>
<EFBFBD> <13>9<EFBFBD>n<EFBFBD> -<2D>)<29>2H<32>2H<32>2T<32><16>y<EFBFBD>-<2D>-<2D>t<EFBFBD>4<>$)<29>)<29>*@<40>*@<40>*C<>*C<>Q<EFBFBD>$G<><04>!<21>/3<>n<EFBFBD>n<EFBFBD>D<EFBFBD>N<EFBFBD>N<EFBFBD>$<24> <09>$)<29>)<29>*@<40>*@<40>9<EFBFBD>*L<>a<EFBFBD>$P<><04>!<21> 3U<01> -<2D><>'<27> <09>2<><1D><1D><1F>:<3A>:<3A>1<>1<>/3<>z<EFBFBD>z<EFBFBD>/E<>/E<>/J<>/J<>D<EFBFBD>,<2C><>*<2A>I<EFBFBD>6<><1D><1C><1D><> <1D>s6<00>.:M7<00>7O<03>;O<02>O<03>O<05>O<03>O<05>O<03>O<03>returnc<00><><00>d|ji}|jr|j|d<|j<00>|j|d<|jr|j|d<|j<00>|j|d<|j
<00>|j
|d<|j r|j |d<|j<00>|j|d<|j<00>|j|d <|j<00>|j|d
<|j<00>|j|d <|jr|j|d <|jr|j|d <|j<00>|j|d<|S)zDConvert to dictionary for JSON serialization, excluding None values.rdrfrYrZr^r_rgrhrirjrkrlrmra)rdrfrYrZr^r_rgrhrirjrkrlrmra)ry<00>results r&<00>to_dictzParagraphData.to_dict<63>sM<00><00>!'<27><14><19><19> 3<><06> <10>;<3B>;<3B>#<23>{<7B>{<7B>F<EFBFBD>8<EFBFBD> <1C> <0F>:<3A>:<3A> !<21>"<22>j<EFBFBD>j<EFBFBD>F<EFBFBD>7<EFBFBD>O<EFBFBD> <0F>><3E>><3E>"&<26>.<2E>.<2E>F<EFBFBD>;<3B> <1F> <0F> <1C> <1C> (<28>%)<29>%6<>%6<>F<EFBFBD>><3E> "<22> <0F> <1B> <1B> '<27>$(<28>$4<>$4<>F<EFBFBD>=<3D> !<21> <0F>><3E>><3E>"&<26>.<2E>.<2E>F<EFBFBD>;<3B> <1F> <0F>><3E>><3E> %<25>"&<26>.<2E>.<2E>F<EFBFBD>;<3B> <1F> <0F>9<EFBFBD>9<EFBFBD> <20>!<21>Y<EFBFBD>Y<EFBFBD>F<EFBFBD>6<EFBFBD>N<EFBFBD> <0F>;<3B>;<3B> "<22>#<23>{<7B>{<7B>F<EFBFBD>8<EFBFBD> <1C> <0F>><3E>><3E> %<25>"&<26>.<2E>.<2E>F<EFBFBD>;<3B> <1F> <0F>:<3A>:<3A>"<22>j<EFBFBD>j<EFBFBD>F<EFBFBD>7<EFBFBD>O<EFBFBD> <0F> <1B> <1B>$(<28>$4<>$4<>F<EFBFBD>=<3D> !<21> <0F> <1C> <1C> (<28>%)<29>%6<>%6<>F<EFBFBD>><3E> "<22><15> rQN)rJrKrLrMrr}<00> ParagraphDictr<74>rPrQr&rSrS<00>s"<00><00>X<>YQ<01>#<23>YQ<01>v <16><1D> rQrSc
<00><><00>eZdZdZededefd<04><04>Zeddededefd<07><05>Zede de
e fd <09><04>Z ed
e de e
ee
effd <0B><04>Zed ed e de
efd<0E><04>Z dd ede
ede
ed
e
e fd<12>Zedeefd<13><04>Zdefd<14>Zdeeeffd<15>Zde dedee fd<18>Zd d<19>Zd d<1A>Zd d<1B>Zedefd<1C><04>Zdefd<1D>Z y)!rzFData structure for shape properties extracted from a PowerPoint shape.<2E>emur~c<00> <00>|dz S)z.Convert EMUs (English Metric Units) to inches.g<00><>+ArP)r<>s r&<00> emu_to_incheszShapeData.emu_to_inches s<00><00><13>X<EFBFBD>~<7E>rQ<00>inches<65>dpic<00><00>t||z<00>S)z&Convert inches to pixels at given DPI.)rO)r<>r<>s r&<00>inches_to_pixelszShapeData.inches_to_pixelss<00><00><13>6<EFBFBD>C<EFBFBD><<3C> <20> rQrgc<00><><00> <0A>tj<00>}||j<00>|jdd<02>|jdd<03>g}|dk(r gd<05>}gd<06>}ngd<07>}dd g}d
d lm}|D]<5D>}||<06>j <00>}|j<00>s<01>*|D]3}|D],} ||<08>| <09><00>z }
|
j<00>s<01>t|
<EFBFBD>cccS<00>5 |j<00>D]u} | j<00>s<01>| jj<00><00> |j<00>jdd<02>} | <0C> vs<01>St<00> fd <0C>|D<00><00>s<01>ht| <0B>ccS<00><>y #ttf$rY<00><>wxYw)z<>Get the font file path for a given font name.
Args:
font_name: Name of the font (e.g., 'Arial', 'Calibri')
Returns:
Path to the font file, or None if not found
<20> <20><00>-<2D>Darwin)z/System/Library/Fonts/z/Library/Fonts/z~/Library/Fonts/)<04>.ttf<74>.otfz.ttcz.dfont)z/usr/share/fonts/truetype/z/usr/local/share/fonts/z ~/.fonts/r<>r<>rrc3<00>@<00>K<00>|]}<01>j|<01><00><01><00>y<00>wr")<01>endswith)r$<00>ext<78>file_name_lowers <20>r&r'z*ShapeData.get_font_path.<locals>.<genexpr>Ps!<00><><00><><00>F<1A>EO<45>c<EFBFBD>O<EFBFBD>4<>4<>S<EFBFBD>9<>Z<EFBFBD>s<00>N)<0F>platform<72>systemr2<00>replace<63>pathlibr<00>
expanduserr-ru<00>iterdir<69>is_filerr<00>any<6E>OSError<6F>PermissionError)rgr<><00>font_variations<6E> font_dirs<72>
extensionsr<00>font_dir<69> font_dir_path<74>variantr<74><00> font_path<74> file_path<74>font_name_lowerr<72>s @r&<00> get_font_pathzShapeData.get_font_pathsy<00><><00><1A><1F><1F>"<22><06> <16> <15>O<EFBFBD>O<EFBFBD> <1D> <15> <1D> <1D>c<EFBFBD>2<EFBFBD> &<26> <15> <1D> <1D>c<EFBFBD>3<EFBFBD> '<27> 
<EFBFBD><0F> <12>X<EFBFBD> <1D><0E>I<EFBFBD>
<<3C>J<EFBFBD><0E>I<EFBFBD>
!<21>&<26>)<29>J<EFBFBD> !<21>!<21>H<EFBFBD> <20><18>N<EFBFBD>5<>5<>7<>M<EFBFBD> <20>'<27>'<27>)<29><18>+<2B><07>%<25>C<EFBFBD> -<2D>7<EFBFBD>)<29>C<EFBFBD>5<EFBFBD>0A<30> A<>I<EFBFBD> <20>'<27>'<27>)<29>"<22>9<EFBFBD>~<7E>-<2D>&<26>+<2B>
<19>!.<2E>!6<>!6<>!8<>I<EFBFBD> <20>(<28>(<28>*<2A>*3<>.<2E>.<2E>*><3E>*><3E>*@<40><0F>*3<>/<2F>/<2F>*;<3B>*C<>*C<>C<EFBFBD><12>*L<><0F>*<2A>o<EFBFBD>=<3D>#<23>F<1A>EO<45>F<1A>C<1A>$'<27>y<EFBFBD>><3E>1<>"9<>"<22>2<14><><1C>_<EFBFBD>-<2D> <19><18> <19>s*<00> #E<02>/>E<02>.E<02> E<02>E<02>E(<05>'E(<05>slidec<00><><00> |jjjj}|j|j
fS#t tf$rYywxYw)z<>Get slide dimensions from slide object.
Args:
slide: Slide object
Returns:
Tuple of (width_emu, height_emu) or (None, None) if not found
<20>NN)<08>part<72>package<67>presentation_part<72> presentation<6F> slide_width<74> slide_heightrvrw)r<><00>prss r&<00>get_slide_dimensionszShapeData.get_slide_dimensionsYsN<00><00> <1E><17>*<2A>*<2A>$<24>$<24>6<>6<>C<>C<>C<EFBFBD><16>?<3F>?<3F>C<EFBFBD>$4<>$4<>4<> 4<><34><1E> <09>*<2A> <1E><1D> <1E>s<00>AA<00>A<03>ArG<00> slide_layoutc<00>j<00> t|d<01>sy|jj}|jD]p}|jj|k(s<01>|jj <00>D]6}d|j vs<01>|jd<04>x}s<01>&t|<05>dz ccSy y#t$rYywxYw)aExtract default font size from slide layout for a placeholder shape.
Args:
shape: Placeholder shape
slide_layout: Slide layout containing the placeholder definition
Returns:
Default font size in points, or None if not found
<20>placeholder_formatN<74>defRPr<50>szgY@)
rnr<><00>type<70> placeholders<72>element<6E>iter<65>tag<61>get<65>floatr9)rGr<><00>
shape_type<EFBFBD>layout_placeholder<65>elemr<6D>s r&<00>get_default_font_sizezShapeData.get_default_font_sizeis<><00><00> <11><1A>5<EFBFBD>"6<>7<><1B><1E>1<>1<>6<>6<>J<EFBFBD>&2<>&?<3F>&?<3F>"<22>%<25>8<>8<>=<3D>=<3D><1A>K<> 2<> :<3A> :<3A> ?<3F> ?<3F> A<><04>#<23>t<EFBFBD>x<EFBFBD>x<EFBFBD>/<2F>4<EFBFBD>8<EFBFBD>8<EFBFBD>D<EFBFBD>><3E>5I<35>R<EFBFBD>5I<35>#(<28><12>9<EFBFBD>u<EFBFBD>#4<>4<>!B<01><1A><14>'@<01><14><><19> <11> <10><13> <11>s4<00> B&<00>>B&<00>+B&<00>:B&<00>B&<00> B&<00>#B&<00>& B2<03>1B2NrHrIc<00><><00>||_d|_|r|j|<04>nd\|_|_d|_d|_t|d<04>r<>|jr<>|jr<>|jjrwt|jj<00>jd<05>djd<07>d|_|r-t|d <09>r!|j||j<00>|_|<02>|nt|d
<EFBFBD>r |jnd}|<03>|nt|d <0B>r |j nd}t#|j%|<05>d <0C>|_t#|j%|<06>d <0C>|_t#|j%t|d <0A>r |j&nd<08>d <0C>|_t#|j%t|d<0E>r |j(nd<08>d <0C>|_||_||_t|d <0A>r |j&nd|_t|d<0E>r |j(nd|_d|_d|_d|_i|_g|_|j=<00>|j?<00>|jA<00>y)a}Initialize from a PowerPoint shape object.
Args:
shape: The PowerPoint shape object (should be pre-validated)
absolute_left: Absolute left position in EMUs (for shapes in groups)
absolute_top: Absolute top position in EMUs (for shapes in groups)
slide: Optional slide object to get dimensions and layout information
r<>r<>N<>is_placeholder<65>.<2E><><EFBFBD><EFBFBD><EFBFBD>r<EFBFBD>rr<><00>left<66>toprc<00>width<74>height)!rG<00>shape_idr<64><00>slide_width_emu<6D>slide_height_emu<6D>placeholder_type<70>default_font_sizernr<>r<>r<>ru<00>splitr<74>r<>r<>r<>rxr<>r<>r<><00>left_emu<6D>top_emu<6D> width_emu<6D>
height_emu<EFBFBD>frame_overflow_bottom<6F>slide_overflow_right<68>slide_overflow_bottom<6F>overlapping_shapes<65>warnings<67>_estimate_frame_overflow<6F>_calculate_slide_overflow<6F>_detect_bullet_issues)ryrGrHrIr<>r<>r<>s r&r}zShapeData.__init__<5F>s@<00><00><1B><04>
<EFBFBD><1F><04> <0A>16<31>D<EFBFBD> %<25> %<25>e<EFBFBD> ,<2C><<3C> 4<><04><1C>d<EFBFBD>3<>
04<30><04><1D>26<32><04><1E> <12>5<EFBFBD>*<2A> +<2B><05>0D<30>0D<30><14>'<27>'<27>E<EFBFBD>,D<>,D<>,I<>,I<><17><05>0<>0<>5<>5<>6<><<3C><<3C>S<EFBFBD>A<>"<22>E<>K<>K<>C<EFBFBD>P<>QR<51>S<><15>%<25>
<19>W<EFBFBD>U<EFBFBD>N<EFBFBD>;<3B>-1<>-G<>-G<><1D>u<EFBFBD>1<>1<>.<16>D<EFBFBD>*<2A><1D>(<28> <1A> '<27><05>v<EFBFBD> 6<>%<25>*<2A>*<2A>A<EFBFBD> <11><1C>'<27> <19>&<26>u<EFBFBD>e<EFBFBD>4<>%<25>)<29>)<29>!<21> <10> !<21><14>!3<>!3<>H<EFBFBD>!=<3D>q<EFBFBD>A<><04> <09><1F><04> 2<> 2<>7<EFBFBD> ;<3B>Q<EFBFBD>?<3F><04><08>!<21> <10> <1E> <1E>g<EFBFBD>e<EFBFBD>W<EFBFBD>.E<>u<EFBFBD>{<7B>{<7B>1<EFBFBD> M<> <0A>
<EFBFBD><04>
<EFBFBD>#<23> <10> <1E> <1E>w<EFBFBD>u<EFBFBD>h<EFBFBD>/G<>u<EFBFBD>|<7C>|<7C>Q<EFBFBD> O<> <0A>
<EFBFBD><04> <0B> !<21><04> <0A><1E><04> <0C>(/<2F><05>w<EFBFBD>(?<3F><15><1B><1B>Q<EFBFBD><04><0E>*1<>%<25><18>*B<>%<25>,<2C>,<2C><01><04><0F>7;<3B><04>"<22>59<35><04>!<21>6:<3A><04>"<22> <0F> <0A><1F>$&<26><04> <0A> <0C>%<25>%<25>'<27> <0C>&<26>&<26>(<28> <0C>"<22>"<22>$rQc<00><00>|jrt|jd<01>sgSg}|jjjD]7}|jj <00>s<01>|j t|<02><00><00>9|S)z1Calculate paragraphs from the shape's text frame.<2E>
text_frame)rGrnr<><00>
paragraphsrdre<00>appendrS)ryr<>rTs r&r<>zShapeData.paragraphs<68>sg<00><00><14>z<EFBFBD>z<EFBFBD><17><14><1A><1A>\<5C>!B<><15>I<EFBFBD><17>
<EFBFBD><1D><1A><1A>.<2E>.<2E>9<>9<>I<EFBFBD><18>~<7E>~<7E>#<23>#<23>%<25><1A>!<21>!<21>-<2D> <09>":<3A>;<3B>:<3A><1A>rQc<00>|<00> t|jd<01>r t|jjd<02>sy|jjjj}t|d<04>syd}|j
rd|j
vrd}|j j<00>D]<5D>}d|jvr|jjd<08>d n |j}||k(s<01>A|j<00>D]/}d
|jvs<01>t|jd
<00>d zccS<00><> y#t$rYywxYw) zIGet default font size from theme text styles or use conservative default.r<>r<><00>r<><00> bodyStyle<6C>TITLE<4C>
titleStyle<EFBFBD>}r<>r<><00>d) rnrGr<>r<><00> slide_masterr<72>r<>r<>r<>r<><00>attribrOr9)ryr<><00>
style_name<EFBFBD>childr<64>r<>s r&<00>_get_default_font_sizez ShapeData._get_default_font_size<7A>s <00><00> <11><17><04>
<EFBFBD>
<EFBFBD>F<EFBFBD>+<2B><07><04>
<EFBFBD>
<EFBFBD><0F><0F><1E>0X<30><19><1F>:<3A>:<3A>?<3F>?<3F>7<>7<>D<>D<>L<EFBFBD><1A><<3C><19>3<><19>%<25>J<EFBFBD><13>$<24>$<24><17>D<EFBFBD>4I<34>4I<34>)I<>)<29>
<EFBFBD>&<26>-<2D>-<2D>2<>2<>4<><05>25<32><15><19><19>2B<32>e<EFBFBD>i<EFBFBD>i<EFBFBD>o<EFBFBD>o<EFBFBD>c<EFBFBD>*<2A>2<EFBFBD>.<2E><05> <09> <09><03><16>*<2A>$<24> %<25>
<EFBFBD>
<EFBFBD> <0C><04><1F>4<EFBFBD>;<3B>;<3B>.<2E>#&<26>t<EFBFBD>{<7B>{<7B>4<EFBFBD>'8<>#9<>S<EFBFBD>#@<40>@<40>!-<2D>5<><12><><19> <11> <10><11> <11>s/<00>6D/<00>6D/<00>0A8D/<00>)!D/<00> D/<00>*D/<00>/ D;<03>:D;c<00>d<00>ddddd<03>}t|d<04>r*|jr|j|j<00>|d<t|d<06>r*|jr|j|j<00>|d<t|d<08>r*|jr|j|j<00>|d <t|d
<EFBFBD>r*|j
r|j|j
<00>|d <|j |d z
|d z
}|j|dz
|dz
}|j|<03>|j|<04>fS) zCGet usable width and height in pixels after accounting for margins.皙<><E79A99><EFBFBD><EFBFBD><EFBFBD>?g<><67><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?)r<><00>bottomr<6D><00>right<68>
margin_topr<EFBFBD><00> margin_bottomr<6D><00> margin_leftr<74><00> margin_rightr<74>) rnr<>r<>r<>r<>r<>r<>r<>r<>)ryr<><00>margins<6E> usable_width<74> usable_heights r&<00>_get_usable_dimensionsz ShapeData._get_usable_dimensions<6E>s(<00><00><1F>$<24><03>c<EFBFBD>J<><07> <13>:<3A>|<7C> ,<2C><1A>1F<31>1F<31>!<21>/<2F>/<2F>
<EFBFBD>0E<EFBFBD>0E<EFBFBD>F<>G<EFBFBD>E<EFBFBD>N<EFBFBD> <12>:<3A><EFBFBD> /<2F>J<EFBFBD>4L<34>4L<34> $<24> 2<> 2<>:<3A>3K<33>3K<33> L<>G<EFBFBD>H<EFBFBD> <1D> <12>:<3A>}<7D> -<2D>*<2A>2H<32>2H<32>"<22>0<>0<><1A>1G<31>1G<31>H<>G<EFBFBD>F<EFBFBD>O<EFBFBD> <12>:<3A>~<7E> .<2E>:<3A>3J<33>3J<33>#<23>1<>1<>*<2A>2I<32>2I<32>J<>G<EFBFBD>G<EFBFBD> <1C><1C>z<EFBFBD>z<EFBFBD>G<EFBFBD>F<EFBFBD>O<EFBFBD>3<>g<EFBFBD>g<EFBFBD>6F<36>F<> <0C><1C> <0B> <0B>g<EFBFBD>e<EFBFBD>n<EFBFBD>4<>w<EFBFBD>x<EFBFBD>7H<37>H<> <0A> <11> !<21> !<21>,<2C> /<2F> <10> !<21> !<21>-<2D> 0<>
<EFBFBD>
rQ<00>line<6E> max_width_pxc<00><00>|sdgS|j||<04><02>|kr|gSg}|jd<03>}d}|D]<}||rdndz|z} |j| |<04><02>|kr| }<07>(|r|j|<07>|}<07>>|r|j|<07>|S)z6Wrap a single line of text to fit within max_width_px.r<>)r`r<>)<03>
textlengthr<EFBFBD>r<>)
ryr<>r<><00>drawr`<00>wrapped<65>words<64> current_line<6E>word<72> test_lines
r&<00>_wrap_text_linezShapeData._wrap_text_lines<><00><00><13><16>4<EFBFBD>K<EFBFBD> <10>?<3F>?<3F>4<EFBFBD>d<EFBFBD>?<3F> +<2B>|<7C> ;<3B><18>6<EFBFBD>M<EFBFBD><15><07><14>
<EFBFBD>
<EFBFBD>3<EFBFBD><0F><05><19> <0C><19>D<EFBFBD>$<24>|<7C><03><12>D<>t<EFBFBD>K<>I<EFBFBD><13><EFBFBD><EFBFBD>y<EFBFBD>t<EFBFBD><EFBFBD>4<> <0C>D<>(<28> <0C><1F><1B>N<EFBFBD>N<EFBFBD><<3C>0<>#<23> <0C><1A> <18> <13>N<EFBFBD>N<EFBFBD><<3C> (<28><16>rQc<00><><00>|jrt|jd<01>sy|jj}|r |jsy|j |<01>\}}|dks|dkryt j dd<05>}tj|<04>}|j<00>}d}t|j<00>D<00>]b\}} | jj<00>s<01>"t| <09>}
|
jxsd} t|
j xs|<06>} d} |j#| <0B>}|r t%j&|| <0C><07>} nt%j*<00>} g}| jj-d<08>D]'}|j/|||| <0A>}|j1|<11><00>)|s<01><>|
j2r|
j2d zd
z }n| d zd
z }|dkDr!|
j4r||
j4d zd
z z }|t7|<0F>|zz }|
j8s<02><01>N||
j8d zd
z z }<07><01>e||kDr"||z
}t;|d z d <0C>}|d kDr||_yyy#t($rt%j*<00>} Y<00><01>wxYw)zGEstimate if text overflows the shape bounds using PIL text measurement.r<>Nr<00>RGB)rr<00>Arial)rs<00>
<EFBFBD>`<00>HgX@rcr<>)rGrnr<>r<>r<>r <00>newr <00>Drawr<77><00> enumeraterdrerSrgrOrhr<>r<00>truetyper9<00> load_defaultr<74>r<00>extendrar^r#r_rxr<>)ryr<><00>usable_width_px<70>usable_height_px<70> dummy_imgr<67>r<><00>total_height_px<70>para_idxrT<00> para_datargrhr`r<><00>all_wrapped_linesr<73>r<00>line_height_px<70> overflow_px<70>overflow_inchess r&r<>z"ShapeData._estimate_frame_overflow2sz<00><00><13>z<EFBFBD>z<EFBFBD><17><14><1A><1A>\<5C>!B<> <12><19>Z<EFBFBD>Z<EFBFBD>*<2A>*<2A>
<EFBFBD><19><1A>!6<>!6<> <12>-1<>,G<>,G<>
<EFBFBD>,S<>)<29><0F>)<29> <1A>a<EFBFBD> <1F>#3<>q<EFBFBD>#8<> <12><1A>I<EFBFBD>I<EFBFBD>e<EFBFBD>V<EFBFBD>,<2C> <09><18>~<7E>~<7E>i<EFBFBD>(<28><04>!<21>7<>7<>9<><19><1C><0F>#,<2C>Z<EFBFBD>-B<>-B<>#C<> <1F>H<EFBFBD>i<EFBFBD><1C>><3E>><3E>'<27>'<27>)<29><18>%<25>i<EFBFBD>0<>I<EFBFBD>"<22>+<2B>+<2B>6<>w<EFBFBD>I<EFBFBD><1B>I<EFBFBD>/<2F>/<2F>D<>3D<33>E<>I<EFBFBD><17>D<EFBFBD><1C>*<2A>*<2A>9<EFBFBD>5<>I<EFBFBD><18>4<>$<24>-<2D>-<2D>i<EFBFBD>i<EFBFBD>H<>D<EFBFBD>!<21>-<2D>-<2D>/<2F><04>!#<23> <1D>!<21><0E><0E>,<2C>,<2C>T<EFBFBD>2<><04><1E>.<2E>.<2E>t<EFBFBD>_<EFBFBD>d<EFBFBD>D<EFBFBD>Q<><07>!<21>(<28>(<28><17>1<>3<>!<21><1C>)<29>)<29>%.<2E>%;<3B>%;<3B>b<EFBFBD>%@<40>2<EFBFBD>%E<>N<EFBFBD>&/<2F><12>^<5E>b<EFBFBD>%8<>N<EFBFBD><1C>a<EFBFBD><<3C>I<EFBFBD>$:<3A>$:<3A>#<23>y<EFBFBD>'=<3D>'=<3D><02>'B<>R<EFBFBD>'G<>G<>O<EFBFBD> <20>3<EFBFBD>'8<>#9<>N<EFBFBD>#J<>J<><0F><1D>(<28>(<28>#<23>y<EFBFBD>'<<3C>'<<3C>r<EFBFBD>'A<>B<EFBFBD>'F<>F<>O<EFBFBD>Y$D<01>^ <1B>-<2D> -<2D>)<29>,<<3C><<3C>K<EFBFBD>#<23>K<EFBFBD>$<24>$6<><01>:<3A>O<EFBFBD><1E><14>%<25>-<<3C><04>*<2A>&<26> .<2E><>A!<21>4<>$<24>1<>1<>3<>D<EFBFBD>4<>s<00>&I<02>I)<05>(I)c<00><><00>|j<00> |j<00>y|j|jz}||jkDr6||jz
}t |j |<02>d<02>}|dkDr||_|j|jz}||jkDr8||jz
}t |j |<02>d<02>}|dkDr||_ yyy)z2Calculate if shape overflows the slide boundaries.Nrcg{<14>G<EFBFBD>z<EFBFBD>?)
r<EFBFBD>r<>r<>r<>rxr<>r<>r<>r<>r<>)ry<00>right_edge_emu<6D> overflow_emur<00>bottom_edge_emus r&r<>z#ShapeData._calculate_slide_overflows<><00><00> <0F> <1F> <1F> '<27>4<EFBFBD>+@<40>+@<40>+H<> <12><1E><1D><1D><14><1E><1E>7<><0E> <19>D<EFBFBD>0<>0<> 0<>)<29>D<EFBFBD>,@<40>,@<40>@<40>L<EFBFBD>#<23>D<EFBFBD>$6<>$6<>|<7C>$D<>a<EFBFBD>H<>O<EFBFBD><1E><14>%<25>,;<3B><04>)<29><1F>,<2C>,<2C><14><1F><1F>8<><0F> <1A>T<EFBFBD>2<>2<> 2<>*<2A>T<EFBFBD>-B<>-B<>B<>L<EFBFBD>#<23>D<EFBFBD>$6<>$6<>|<7C>$D<>a<EFBFBD>H<>O<EFBFBD><1E><14>%<25>-<<3C><04>*<2A>&<26> 3rQc<00>\<00><04>|jrt|jd<01>sy|jj}|r |jsygd<03>}|jD]P}|jj <00><00><04>s<01> t <00>fd<04>|D<00><00>s<01>5|jjd<05>yy)z4Detect bullet point formatting issues in paragraphs.r<>N)u•u●u○c3<00>F<00>K<00>|]}<01>j|dz<00><00><01><00>y<01>w)r<>N)<01>
startswith)r$<00>symbolrds <20>r&r'z2ShapeData._detect_bullet_issues.<locals>.<genexpr><3E>s<00><><00><><00>W<><0E>f<EFBFBD>D<EFBFBD>O<EFBFBD>O<EFBFBD>F<EFBFBD>S<EFBFBD>L<EFBFBD>9<><0E>s<00>!z2manual_bullet_symbol: use proper bullet formatting) rGrnr<>r<>rdrer<>r<>r<>)ryr<><00>bullet_symbolsrTrds @r&r<>zShapeData._detect_bullet_issues<65>s<><00><><00><13>z<EFBFBD>z<EFBFBD><17><14><1A><1A>\<5C>!B<> <12><19>Z<EFBFBD>Z<EFBFBD>*<2A>*<2A>
<EFBFBD><19><1A>!6<>!6<> <12>/<2F><0E>#<23>.<2E>.<2E>I<EFBFBD><1C>><3E>><3E>'<27>'<27>)<29>D<EFBFBD><13><03>W<><0E>W<>W<><14> <0A> <0A>$<24>$<24>H<><12><16>/rQc<00><><00>|jduxsR|jduxsB|jduxs2t|j<00>dkDxst|j
<00>dkDS)z?Check if shape has any issues (overflow, overlap, or warnings).Nr)r<>r<>r<>r#r<>r<>)rys r&<00>has_any_issueszShapeData.has_any_issues<65>so<00><00> <11> &<26> &<26>d<EFBFBD> 2<> &<26><13>(<28>(<28><04>4<> &<26><13>)<29>)<29><14>5<> &<26><13>4<EFBFBD>*<2A>*<2A>+<2B>a<EFBFBD>/<2F> &<26><13>4<EFBFBD>=<3D>=<3D>!<21>A<EFBFBD>%<25> 
rQc<00>j<00>|j|j|j|jd<01>}|jr|j|d<|j
r|j
|d<i}|j <00>d|j i|d<i}|j<00>|j|d<|j<00>|j|d<|r||d<|r||d<|jrd |ji|d
<|jr|j|d <|jD<00>cgc]}|j<00><00><02>c}|d <|Scc}w) z-Convert to dictionary for JSON serialization.)r<>r<>r<>r<>r<>r<><00>overflow_bottom<6F>frame<6D>overflow_rightr<74><00>overflowr<77><00>overlapr<70>r<>) r<>r<>r<>r<>r<>r<>r<>r<>r<>r<>r<>r<>r<>)ryr<><00> overflow_data<74>slide_overflow<6F>paras r&r<>zShapeData.to_dict<63>sL<00><00><19>I<EFBFBD>I<EFBFBD><17>8<EFBFBD>8<EFBFBD><19>Z<EFBFBD>Z<EFBFBD><1A>k<EFBFBD>k<EFBFBD> 
<EFBFBD><06> <10> <20> <20>)-<2D>)><3E>)><3E>F<EFBFBD>%<25> &<26> <0F> !<21> !<21>*.<2E>*@<40>*@<40>F<EFBFBD>&<26> '<27><1B> <0A> <10> %<25> %<25> 1<>&7<><14>9S<39>9S<39>%T<>M<EFBFBD>'<27> "<22><1C><0E> <0F> $<24> $<24> 0<>/3<>/H<>/H<>N<EFBFBD>+<2B> ,<2C> <0F> %<25> %<25> 1<>04<30>0J<30>0J<30>N<EFBFBD>,<2C> -<2D> <19>%3<>M<EFBFBD>'<27> "<22> <19>!.<2E>F<EFBFBD>:<3A> <1E> <10> "<22> "<22>!5<>t<EFBFBD>7N<37>7N<37> O<>F<EFBFBD>9<EFBFBD> <1D> <10>=<3D>=<3D>!%<25><1D><1D>F<EFBFBD>:<3A> <1E><@<01>?<3F>?<3F>K<>?<3F>4<EFBFBD><04> <0C> <0C><0E>?<3F>K<><06>|<7C><1C><15> <0A><> Ls<00>D0)r
)NNN)r~N)!rJrKrLrM<00> staticmethodrOr<>r<>r<>rur r<>r<00>tupler<65>rr<>r}<00>propertyrrSr<>r<>r
r<>rr<>r<>r<><00>boolr&<00> ShapeDictr<74>rPrQr&rr
s<><00><00>P<><11><1E>3<EFBFBD><1E>5<EFBFBD><1E><12><1E><12>!<21><15>!<21>S<EFBFBD>!<21>#<23>!<21><12>!<21><12>?<14><13>?<14><18>#<23><1D>?<14><12>?<14>B<12> <1E>C<EFBFBD> <1E>E<EFBFBD>(<28>3<EFBFBD>-<2D><18>#<23><1D>2N<32>,O<> <1E><12> <1E><12><14>Y<EFBFBD><14>c<EFBFBD><14>h<EFBFBD>u<EFBFBD>o<EFBFBD><14><12><14>:(,<2C>&*<2A>#<23> N%<25><18>N%<25> <20><03>}<7D>N%<25><1F>s<EFBFBD>m<EFBFBD> N%<25>
<18><03>}<7D> N%<25>`<0E> <1A>D<EFBFBD><1D>/<2F> <1A><0E> <1A><12><03><12>:
<EFBFBD>E<EFBFBD>#<23>s<EFBFBD>(<28>O<EFBFBD>
<EFBFBD>2<17>C<EFBFBD><17>s<EFBFBD><17>4<EFBFBD>PS<50>9<EFBFBD><17>8K=<3D>Z=<3D>*<16>*<0E>
<EFBFBD><04>
<EFBFBD><0E>
<EFBFBD>/<16><19>/rQrGr~c<00><><00>t|d<01>r |jsy|jjj<00>}|syt|d<03>r<>|jr<>|j
ru|j
j r_t|j
j <00>jd<04>djd<06>d}|dk(ry|d k(r|j<00>ryy
) z2Check if a shape contains meaningful text content.r<>Fr<46>r<>r<>r<>r<00> SLIDE_NUMBER<45>FOOTERT)
rnr<>rdrer<>r<>r<>rur<><00>isdigit)rGrdr<>s r&<00>is_valid_shaper9<00>s<><00><00> <13>5<EFBFBD>,<2C> '<27>u<EFBFBD>/?<3F>/?<3F><14> <10> <1B> <1B> <20> <20> &<26> &<26> (<28>D<EFBFBD> <0F><14><0F>u<EFBFBD>&<26>'<27>E<EFBFBD>,@<40>,@<40> <10> #<23> #<23><05>(@<40>(@<40>(E<>(E<><13>E<EFBFBD>,<2C>,<2C>1<>1<>2<>8<>8<><13>=<3D>b<EFBFBD>A<>G<>G<><03>L<>Q<EFBFBD>O<> <1D> <20>><3E>1<><1C><1F>8<EFBFBD>+<2B><04> <0C> <0C><0E><1C> rQ<00> parent_left<66>
parent_topc<00><><00>t|d<01>rog}t|d<02>r |jnd}t|d<04>r |jnd}||z}||z}|jD]}|j t |||<07><00><00> |St |<00>rIt|d<02>r |jnd} t|d<04>r |jnd}
t||| z||
z<00><05>gSgS)aRecursively collect all shapes with valid text, calculating absolute positions.
For shapes within groups, their positions are relative to the group.
This function calculates the absolute position on the slide by accumulating
parent group offsets.
Args:
shape: The shape to process
parent_left: Accumulated left offset from parent groups (in EMUs)
parent_top: Accumulated top offset from parent groups (in EMUs)
Returns:
List of ShapeWithPosition objects with absolute positions
r%r<>rr<>)rGrHrI)rnr<>r<>r%r<00>&collect_shapes_with_absolute_positionsr9rF) rGr:r;r<><00>
group_left<EFBFBD> group_top<6F>abs_group_left<66> abs_group_topr<70><00>
shape_left<EFBFBD> shape_tops r&r=r=<00>s<><00><00>"<0F>u<EFBFBD>h<EFBFBD><1F><13><06>#*<2A>5<EFBFBD>&<26>#9<>U<EFBFBD>Z<EFBFBD>Z<EFBFBD>q<EFBFBD>
<EFBFBD>!(<28><15><05>!6<>E<EFBFBD>I<EFBFBD>I<EFBFBD>A<EFBFBD> <09>%<25>z<EFBFBD>1<><0E>"<22>Y<EFBFBD>.<2E> <0A><1B>\<5C>\<5C>E<EFBFBD> <12>M<EFBFBD>M<EFBFBD>6<><19>><3E>=<3D><12> <0E>"<22> <16> <0A><16>e<EFBFBD><1C>#*<2A>5<EFBFBD>&<26>#9<>U<EFBFBD>Z<EFBFBD>Z<EFBFBD>q<EFBFBD>
<EFBFBD>!(<28><15><05>!6<>E<EFBFBD>I<EFBFBD>I<EFBFBD>A<EFBFBD> <09> <1E><1B>)<29>J<EFBFBD>6<>'<27>)<29>3<> <0E>
<EFBFBD>
<EFBFBD> <0E>IrQr%c<00>X<00>|s|St|d<01><00><02>}g}|dg}|dj}|ddD][}t|j|z
<00>dkr|j|<04><00>0|j t|d<07><00><02><00>|g}|j}<03>]|j t|d<08><00><02><00>|S) z<>Sort shapes by visual position (top-to-bottom, left-to-right).
Shapes within 0.5 inches vertically are considered on the same row.
c<00>2<00>|j|jfSr")r<>r<><00><01>ss r&<00><lambda>z)sort_shapes_by_position.<locals>.<lambda><s<00><00>1<EFBFBD>5<EFBFBD>5<EFBFBD>!<21>&<26>&<26>/rQ)<01>keyrrNg<00>?c<00><00>|jSr"<00>r<>rFs r&rHz)sort_shapes_by_position.<locals>.<lambda>Hs<00><00>A<EFBFBD>F<EFBFBD>FrQc<00><00>|jSr"rKrFs r&rHz)sort_shapes_by_position.<locals>.<lambda>Ms<00><00>A<EFBFBD>F<EFBFBD>FrQ)<05>sortedr<64><00>absr<73>r)r%r<><00>row<6F>row_toprGs r&<00>sort_shapes_by_positionrQ3s<><00><00>
<12><15> <0A><14>F<EFBFBD> 9<> :<3A>F<EFBFBD><10>F<EFBFBD> <11>!<21>9<EFBFBD>+<2B>C<EFBFBD><14>Q<EFBFBD>i<EFBFBD>m<EFBFBD>m<EFBFBD>G<EFBFBD><17><01><02><1A><05> <0E>u<EFBFBD>y<EFBFBD>y<EFBFBD>7<EFBFBD>"<22> #<23>s<EFBFBD> *<2A> <0F>J<EFBFBD>J<EFBFBD>u<EFBFBD> <1D> <13>M<EFBFBD>M<EFBFBD>&<26><13>*:<3A>;<3B> <<3C><18>'<27>C<EFBFBD><1B>i<EFBFBD>i<EFBFBD>G<EFBFBD><1C> <0B>M<EFBFBD>M<EFBFBD>&<26><13>"2<>3<>4<> <11>MrQ<00>rect1<74>rect2<74> tolerancec<00><><00>|\}}}}|\}}} }
t||z|| z<00>t||<07>z
} t||z||
z<00>t||<08>z
} | |kDr| |kDr| | z} dt| d<02>fSy)a<>Calculate if and how much two rectangles overlap.
Args:
rect1: (left, top, width, height) of first rectangle in inches
rect2: (left, top, width, height) of second rectangle in inches
tolerance: Minimum overlap in inches to consider as overlapping (default: 0.05")
Returns:
Tuple of (overlaps, overlap_area) where:
- overlaps: True if rectangles overlap by more than tolerance
- overlap_area: Area of overlap in square inches
Trc)Fr)<03>min<69>maxrx)rRrSrT<00>left1<74>top1<70>w1<77>h1<68>left2<74>top2<70>w2<77>h2<68> overlap_width<74>overlap_height<68> overlap_areas r&<00>calculate_overlaprcQs<><00><00>" <20><17>E<EFBFBD>4<EFBFBD><12>R<EFBFBD><1F><17>E<EFBFBD>4<EFBFBD><12>R<EFBFBD><18><05><02>
<EFBFBD>E<EFBFBD>B<EFBFBD>J<EFBFBD>/<2F>#<23>e<EFBFBD>U<EFBFBD>2C<32>C<>M<EFBFBD><18><14><02><19>D<EFBFBD>2<EFBFBD>I<EFBFBD>.<2E><13>T<EFBFBD>4<EFBFBD><1F>@<40>N<EFBFBD><15>y<EFBFBD> <20>^<5E>i<EFBFBD>%?<3F>$<24>~<7E>5<> <0C><13>U<EFBFBD><<3C><11>+<2B>+<2B>+<2B> rQc<00><00>t|<00>}t|<01>D]<5D>}t|dz|<01>D]<5D>}||}||}|js Jd|<02>d<03><03><00>|js Jd|<03>d<03><03><00>|j|j|j
|j f}|j|j|j
|j f}t||<07>\}} |s<01><>| |j|j<| |j|j<<00><><00><>y)aJDetect overlapping shapes and update their overlapping_shapes dictionaries.
This function requires each ShapeData to have its shape_id already set.
It modifies the shapes in-place, adding shape IDs with overlap areas in square inches.
Args:
shapes: List of ShapeData objects with shape_id attributes set
rzShape at index z has no shape_idN) r#<00>ranger<65>r<>r<>r<>r<>rcr<>)
r%<00>n<>i<>j<>shape1<65>shape2rRrS<00>overlapsrbs
r&<00>detect_overlapsrlrs<><00><00> <0C>F<EFBFBD> <0B>A<EFBFBD><13>1<EFBFBD>X<EFBFBD><01><16>q<EFBFBD>1<EFBFBD>u<EFBFBD>a<EFBFBD><1F>A<EFBFBD><1B>A<EFBFBD>Y<EFBFBD>F<EFBFBD><1B>A<EFBFBD>Y<EFBFBD>F<EFBFBD><1A>?<3F>?<3F> I<>o<EFBFBD>a<EFBFBD>S<EFBFBD>8H<38>$I<> I<>?<3F><19>?<3F>?<3F> I<>o<EFBFBD>a<EFBFBD>S<EFBFBD>8H<38>$I<> I<>?<3F><1B>[<5B>[<5B>&<26>*<2A>*<2A>f<EFBFBD>l<EFBFBD>l<EFBFBD>F<EFBFBD>M<EFBFBD>M<EFBFBD>J<>E<EFBFBD><1B>[<5B>[<5B>&<26>*<2A>*<2A>f<EFBFBD>l<EFBFBD>l<EFBFBD>F<EFBFBD>M<EFBFBD>M<EFBFBD>J<>E<EFBFBD>%6<>u<EFBFBD>e<EFBFBD>%D<> "<22>H<EFBFBD>l<EFBFBD><17>=I<><06>)<29>)<29>&<26>/<2F>/<2F>:<3A>=I<><06>)<29>)<29>&<26>/<2F>/<2F>:<3A>!!<21>rQ<00> pptx_pathr<68>rc
<00>f<00>|<01>tt|<00><00>}i}t|j<00>D]<5D>\}}g}|jD]}|j t |<07><00><00>|s<01>6|D<00>cgc].}t|j|j|j|<05><00><02>0} }t| <09>}
t|
<EFBFBD>D]\} } d| <0B><00>| _ <00>t|
<EFBFBD>dkDr t|
<EFBFBD>|r|
D<00> cgc]} | js<01>| <0A><02>}
} |
s<01><>|
D<00> cic]} | j| <0C><02>c} |d|<04><00><<00><>|Scc}wcc} wcc} w)aNExtract text content from all slides in a PowerPoint presentation.
Args:
pptx_path: Path to the PowerPoint file
prs: Optional Presentation object to use. If not provided, will load from pptx_path.
issues_only: If True, only include shapes that have overflow or overlap issues
Returns a nested dictionary: {slide-N: {shape-N: ShapeData}}
Shapes are sorted by visual position (top-to-bottom, left-to-right).
The ShapeData objects contain the full shape information and can be
converted to dictionaries for JSON serialization using to_dict().
zshape-rzslide-)rrur<00>slidesr%rr=rrGrHrIrQr<>r#rlr&)rmr<>rr?<00> slide_idxr<78><00>shapes_with_positionsrG<00>swp<77>shape_data_list<73> sorted_shapes<65>idx<64>
shape_data<EFBFBD>sds r&r3r3<00>sd<00><00> <0B>{<7B><1A>3<EFBFBD>y<EFBFBD>><3E>*<2A><03>!<21>I<EFBFBD>%<25>c<EFBFBD>j<EFBFBD>j<EFBFBD>1<><18> <09>5<EFBFBD> "<22><1D><1A>\<5C>\<5C>E<EFBFBD> !<21> (<28> (<28>)O<>PU<50>)V<> W<>"<22>%<25> <14>-<2D>
<EFBFBD>-<2D><03> <16><13> <09> <09><13>!<21>!<21><13> <20> <20><15>  <0E> -<2D> <18>
<EFBFBD>0<><0F>@<40> <0A>(<28><1D>7<>O<EFBFBD>C<EFBFBD><1A>$*<2A>3<EFBFBD>%<25>.<2E>J<EFBFBD> <1F> 8<> <0F>}<7D> <1D><01> !<21> <1B>M<EFBFBD> *<2A> <17>*7<>M<>-<2D>B<EFBFBD>2<EFBFBD>;L<>;L<>R<EFBFBD>-<2D>M<EFBFBD>M<><1C> <14>?L<01>+
<EFBFBD>>K<>
<EFBFBD>J<EFBFBD> <1F> <1F><1A> +<2B>m<EFBFBD>+
<EFBFBD> <09>F<EFBFBD>9<EFBFBD>+<2B>&<26>'<27>K2<>R <15><14><>?
<EFBFBD><EFBFBD>*N<01><> +
s<00>(3D$<06>$D)<06>6D)<06>D.c<00><><00>t||<01><01>}i}|j<00>D]:\}}|j<00>D<00><06>cic]\}}||j<00><00><02>c}}||<<00><|Scc}}w)a<>Extract text inventory and return as JSON-serializable dictionaries.
This is a convenience wrapper around extract_text_inventory that returns
dictionaries instead of ShapeData objects, useful for testing and direct
JSON serialization.
Args:
pptx_path: Path to the PowerPoint file
issues_only: If True, only include shapes that have overflow or overlap issues
Returns:
Nested dictionary with all data serialized for JSON
r)r3<00>itemsr<73>)rmrr?<00>dict_inventory<72> slide_keyr%<00> shape_keyrvs r&<00>get_inventory_as_dictr}<00>sq<00><00>'<27>y<EFBFBD>k<EFBFBD>J<>I<EFBFBD>%'<27>N<EFBFBD>&<26>_<EFBFBD>_<EFBFBD>.<2E><19> <09>6<EFBFBD>IO<49><1C><1C><1E>%
<EFBFBD>IW<EFBFBD>0E<EFBFBD> <09>:<3A>I<EFBFBD>z<EFBFBD>)<29>)<29>+<2B> +<2B><1E>%
<EFBFBD><0E>y<EFBFBD>!<21>/<2F>
<1A><19><> %
s<00>Ar?r@c<00>&<00>i}|j<00>D]:\}}|j<00>D<00><05>cic]\}}||j<00><00><02>c}}||<<00><t|dd<02><03>5}tj||dd<05><06>ddd<07>ycc}}w#1swYyxYw)z<>Save inventory to JSON file with proper formatting.
Converts ShapeData objects to dictionaries for JSON serialization.
<20>wzutf-8)<01>encodingrcF)<02>indent<6E> ensure_asciiN)ryr<><00>open<65>json<6F>dump)r?r@<00>json_inventoryr{r%r|rv<00>fs r&r6r6<00>s<><00><00> %'<27>N<EFBFBD>&<26>_<EFBFBD>_<EFBFBD>.<2E><19> <09>6<EFBFBD>IO<49><1C><1C><1E>%
<EFBFBD>IW<EFBFBD>0E<EFBFBD> <09>:<3A>I<EFBFBD>z<EFBFBD>)<29>)<29>+<2B> +<2B><1E>%
<EFBFBD><0E>y<EFBFBD>!<21>/<2F>
<0E>k<EFBFBD>3<EFBFBD><17> 1<>Q<EFBFBD> <0C> <09> <09>.<2E>!<21>A<EFBFBD>E<EFBFBD>B<>
2<EFBFBD> 1<><31> %
<EFBFBD>
2<EFBFBD> 1<>s<00>B<08>B<03>B<07>__main__)rr)r<>)NF)F)0rMr(r<>r<>r/<00> dataclassesrr<>r<00>typingrrrr r
r <00>PILr r r<00>pptxr<00>pptx.enum.textr<00>pptx.shapes.baserrurOr<>r3<00> JsonValuer<65>r4<00> InventoryData<74> InventoryDictrDrFrSrr9r=rQrcrlr3r}r6rJrPrQr&<00><module>r<>sO<00><01><04>.<10> <0B><0F>
<EFBFBD>!<21><18>:<3A>:<3A>+<2B>+<2B><1D>#<23>&<26> <12>#<23>s<EFBFBD>E<EFBFBD>4<EFBFBD><14>-<2D> .<2E> <09><14>S<EFBFBD>)<29>^<5E>$<24> <0A> <10><07><15>s<EFBFBD>E<EFBFBD>4<EFBFBD><14>m<EFBFBD>!4<>d<EFBFBD>3<EFBFBD>i<EFBFBD><14>c<EFBFBD>3<EFBFBD>h<EFBFBD><1E>QU<51>U<> V<>V<> <02> <09><15><07><14>c<EFBFBD>;<3B><1E> <1F><1F><02> <0A><15>S<EFBFBD>$<24>s<EFBFBD>I<EFBFBD>~<7E>.<2E>.<2E>/<2F> <0A>K<14>\ <0B><16><16> <0B><16>~<16>~<16>BY<16>Y<16>x<10>)<29><10><04><10>2?@<01>2<0E> <14>2<0E>#&<26>2<0E>8;<3B>2<0E> <09>
<1B><1C>2<0E>j<12>D<EFBFBD><19>O<EFBFBD><12><04>Y<EFBFBD><0F><12>B<1C><14> <10><15><05>u<EFBFBD>e<EFBFBD>+<2B> ,<2C><14> <10><15><05>u<EFBFBD>e<EFBFBD>+<2B> ,<2C><14><15><14> <0B>4<EFBFBD><15>;<3B><17> <14>BJ<01>D<EFBFBD><19>O<EFBFBD>J<01><04>J<01>BEJ<01><<15><13><<15>"<22>3<EFBFBD>-<2D><<15>=A<><<15><12><<15>~<1A>T<EFBFBD><1A><04><1A><1D><1A>4 C<01>m<EFBFBD> C<01>$<24> C<01>4<EFBFBD> C<01>  <0C>z<EFBFBD><19><08>F<EFBFBD>rQ