
    j$                       U d Z ddlmZ ddlZddlZddlmZmZmZm	Z	m
Z
mZmZmZmZmZmZ ddlZddlmZmZ ddlmZmZmZmZmZmZmZmZ ddlmZ eege f         Z!de"d	<   	 d
Z#de"d<   	  G d d          Z$ G d d          Z% G d d          Z& G d d          Z' G d d          Z( G d d          Z) G d d          Z* G d d          Z+d#dZ,d#dZ-d$d"Z.dS )%z@Chunking objects not specific to a particular chunking strategy.    )annotationsN)AnyCallableDefaultDictDictIterableIteratorListOptionalSequenceTuplecast)Self	TypeAlias)CompositeElementConsolidationStrategyElementElementMetadataRegexMetadataTable
TableChunkTitle)lazypropertyr   BoundaryPredicatezTablePreChunk | TextPreChunkPreChunkc                     e Zd ZdZ	 	 	 	 	 	 d"d#dZe	 	 	 	 	 	 d"d$d            Zed%d            Zed%d            Z	ed&d            Z
ed%d            Zed%d            Zed'd            Zed(d            Zed)d            Zd*d!ZdS )+ChunkingOptionsa
  Specifies parameters of optional chunking behaviors.

    Parameters
    ----------
    max_characters
        Hard-maximum text-length of chunk. A chunk longer than this will be split mid-text and be
        emitted as two or more chunks.
    new_after_n_chars
        Preferred approximate chunk size. A chunk composed of elements totalling this size or
        greater is considered "full" and will not be enlarged by adding another element, even if it
        will fit within the remaining `max_characters` for that chunk. Defaults to `max_characters`
        when not specified, which effectively disables this behavior. Specifying 0 for this
        argument causes each element to appear in a chunk by itself (although an element with text
        longer than `max_characters` will be still be split into two or more chunks).
    multipage_sections
        Indicates that page-boundaries should not be respected while chunking, i.e. elements
        appearing on two different pages can appear in the same chunk.
    combine_text_under_n_chars
        Provides a way to "recombine" small chunks formed by breaking on a semantic boundary. Only
        relevant for a chunking strategy that specifies higher-level semantic boundaries to be
        respected, like "section" or "page". Recursively combines two adjacent pre-chunks when the
        first pre-chunk is smaller than this threshold. "Recursively" here means the resulting
        pre-chunk can be combined with the next pre-chunk if it is still under the length threshold.
        Defaults to `max_characters` which combines chunks whenever space allows. Specifying 0 for
        this argument suppresses combining of small chunks. Note this value is "capped" at the
        `new_after_n_chars` value since a value higher than that would not change this parameter's
        effect.
    overlap
        Specifies the length of a string ("tail") to be drawn from each chunk and prefixed to the
        next chunk as a context-preserving mechanism. By default, this only applies to split-chunks
        where an oversized element is divided into multiple chunks by text-splitting.
    text_splitting_separators
        A sequence of strings like `("
", " ")` to be used as target separators during
        text-splitting. Text-splitting only applies to splitting an oversized element into two or
        more chunks. These separators are tried in the specified order until one is found in the
        string to be split. The default separator is `""` which matches between any two characters.
        This separator should not be specified in this sequence because it is always the separator
        of last-resort. Note that because the separator is removed during text-splitting, only
        whitespace character sequences are suitable.
    N  Tr    combine_text_under_n_charsOptional[int]max_charactersintmultipage_sectionsboolnew_after_n_charsoverlaptext_splitting_separatorsSequence[str]c                Z    || _         || _        || _        || _        || _        || _        d S N)_combine_text_under_n_chars_arg_max_characters_multipage_sections_new_after_n_chars_arg_overlap_text_splitting_separators)selfr    r"   r$   r&   r'   r(   s          d/lsinfo/ai/hellotax_ai/base_platform/venv/lib/python3.11/site-packages/unstructured/chunking/base.py__init__zChunkingOptions.__init__Z   s:     0J,-#5 &7#*C'''    returnr   c                N     | ||||||          }|                                  |S )zrConstruct validated instance.

        Raises `ValueError` on invalid arguments like overlap > max_chars.
        )	_validate)clsr    r"   r$   r&   r'   r(   r2   s           r3   newzChunkingOptions.newj   s>     s&%
 
 	r5   c                X    | j         }| j        }| j        }|||k    r|n|}||k    r|n|S )a  Combine consecutive text pre-chunks if former is smaller than this and both will fit.

        - Does not combine table chunks with text chunks even if they would both fit in the
          chunking window.
        - Does not combine text chunks if together they would exceed the chunking window.
        - Defaults to `max_characters` when not specified.
        - Is reduced to `new_after_n_chars` when it exceeds that value.
        )r-   soft_maxr,   )r2   r"   r<   argr    s        r3   r    z*ChunkingOptions.combine_text_under_n_chars   sM     -=2 8;{cNFZFZ^^`c" 6@@xxF``r5   c                    | j         S )zThe maximum size for a chunk.

        A pre-chunk will only exceed this size when it contains exactly one element which by itself
        exceeds this size. Such a pre-chunk is subject to mid-text splitting later in the chunking
        process.
        )r-   r2   s    r3   hard_maxzChunkingOptions.hard_max   s     ##r5   c                    | j         S )z0When False, break pre-chunks on page-boundaries.)r.   r?   s    r3   r$   z"ChunkingOptions.multipage_sections   s     ''r5   c                    | j         S )zThe number of characters to overlap text when splitting chunks mid-text.

        The actual overlap will not exceed this number of characters but may be less as required to
        respect splitting-character boundaries.
        )r0   r?   s    r3   r'   zChunkingOptions.overlap   s     }r5   c                B    | j         }| j        }||dk     s||k    r|n|S )zA pre-chunk of this size or greater is considered full.

        ??? Is a value of 0 valid? It would produce the behavior: "put each element into its own
        chunk".
        Nr   )r-   r/   )r2   	max_charsr&   s      r3   r<   zChunkingOptions.soft_max   sC     (	 7 ")->-B-BFWZcFcFc I"	
r5    Callable[[str], Tuple[str, str]]c                     t          |           S )a  A text-splitting function suitable for splitting the text of an oversized pre-chunk.

        The function is pre-configured with the chosen chunking window size and any other applicable
        options specified by the caller as part of this chunking-options instance.
        )_TextSplitterr?   s    r3   splitzChunkingOptions.split   s     T"""r5   strc                    dS )a;  The string to insert between elements when concatenating their text for a chunk.

        Right now this is just "

" (a blank line in plain text), but having this here rather
        than as a module-level constant provides a way for us to easily make it user-configurable
        in future if we want to.
        z

r   r?   s    r3   text_separatorzChunkingOptions.text_separator   s	     vr5   Tuple[str, ...]c                *    t          | j                  S )zLSequence of text-splitting target strings to be used in order of preference.)tupler1   r?   s    r3   r(   z)ChunkingOptions.text_splitting_separators   s     T4555r5   Nonec                   | j         }|dk    rt          d|           | j        }||dk     rt          d|           | j        }||dk     rt          d|           | j        |k    rt          d| j                   dS )z5Raise ValueError if requestion option-set is invalid.r   z+'max_characters' argument must be > 0, got Nz8'combine_text_under_n_chars' argument must be >= 0, got z/'new_after_n_chars' argument must be >= 0, got z0'overlap' must be less than max_characters, got )r-   
ValueErrorr,   r/   r0   )r2   r"   r    r&   s       r3   r8   zChunkingOptions._validate   s    -Q_~__``` &*%I"%16PST6T6T525 5   !7(->-B-BYFWYY   =N**cTXTaccddd +*r5   )Nr   TNr   r   )r    r!   r"   r#   r$   r%   r&   r!   r'   r#   r(   r)   )r    r!   r"   r#   r$   r%   r&   r!   r'   r#   r(   r)   r6   r   r6   r#   )r6   r%   )r6   rE   r6   rI   )r6   rL   r6   rO   )__name__
__module____qualname____doc__r4   classmethodr:   r   r    r@   r$   r'   r<   rH   rK   r(   r8   r   r5   r3   r   r   0   s       ' 'V 59!#'+/35D D D D D   59!#'+/35    [0 a a a \a( $ $ $ \$ ( ( ( \(    \ 
 
 
 \
 # # # \#    \ 6 6 6 \6e e e e e er5   r   c                  B    e Zd ZdZddZdd	Zedd            ZddZdS )rG   a  Provides a text-splitting function configured on construction.

    Text is split on the best-available separator, falling-back from the preferred separator
    through a sequence of alternate separators.

    - The separator is removed by splitting so only whitespace strings are suitable separators.
    - A "blank-line" ("

") is unlikely to occur in an element as it would have been used as an
      element boundary during partitioning.

    This is a *callable* object. Constructing it essentially produces a function:

        split = _TextSplitter(opts)
        fragment, remainder = split(s)

    This allows it to be configured with length-options etc. on construction and used throughout a
    chunking operation on a given element-stream.
    optsr   c                    || _         d S r+   )_optsr2   r[   s     r3   r4   z_TextSplitter.__init__  s    


r5   srI   r6   Tuple[str, str]c                   | j         j        }t          |          |k    r|dfS | j        D ]k\  }}|                     |||          \  }}|r t          |          t          |          k    rB|                                |                                fc S |d|                                         ||| j         j        z
  d                                         fS )a  Return pair of strings split from `s` on the best match of configured patterns.

        The first string is the split, the second is the remainder of the string. The split string
        will never be longer than `maxlen`. The separators are tried in order until a match is
        found. The last separator is "" which matches between any two characters so there will
        always be a split.

        The separator is removed and does not appear in the split or remainder.

        An `s` that is already less than the maximum length is returned unchanged with no remainder.
        This allows this function to be called repeatedly with the remainder until it is consumed
        and returns a remainder of "".
         N)r]   r@   len	_patterns_split_from_maxlenrstriplstripr'   )r2   r_   maxlenpsep_lenfragment	remainders          r3   __call__z_TextSplitter.__call__  s     $q66Vb5L. 	9 	9JAw #'"9"9!Wa"H"HHi  y>>SVV++??$$i&6&6&8&88888
 &z  ""Aftz/A&A&C&C$D$K$K$M$MMMr5   *Tuple[Tuple[regex.Pattern[str], int], ...]c                L    | j         j        }t          d |D                       S )a  Sequence of (pattern, len) pairs to match against.

        Patterns appear in order of preference, those following are "fall-back" patterns to be used
        if no match of a prior pattern is found.

        NOTE these regexes search *from the end of the string*, which is what the "(?r)" bit
        specifies. This is much more efficient than starting at the beginning of the string which
        could result in hundreds of matches before the desired one.
        c              3  b   K   | ]*}t          j        d |           t          |          fV  +dS )z(?r)N)regexcompilerc   ).0seps     r3   	<genexpr>z*_TextSplitter._patterns.<locals>.<genexpr>?  s=      SSemL3LL113s88<SSSSSSr5   )r]   r(   rN   )r2   
separatorss     r3   rd   z_TextSplitter._patterns3  s+     Z9
SS
SSSSSSr5   patternregex.Pattern[str]rj   r#   c                   | j         j        | j         j        }}|                    ||dz   ||z             }|d|fS |                                \  }}d}	|d|                                         }
||d                                         }|t          |	          k    r|
|fS |t          |	          z
  }|
| d                                         }||	z   |z   }|
|fS )a  Return (split, remainder) pair split from `s` on the right-most match before `maxlen`.

        Returns `"", s` if no suitable match was found. Also returns `"", s` if splitting on this
        separator produces a split shorter than the required overlap (which would produce an
        infinite loop).

        `split` will never be longer than `maxlen` and there is no longer split available using
        `pattern`.

        The separator is removed and does not appear in either the split or remainder.
           )posendposNrb    )r]   r@   r'   searchspanrf   rg   rc   )r2   rw   rj   r_   rh   r'   matchmatch_start	match_end	separatorrk   raw_remaindertail_lentailoverlapped_remainders                  r3   re   z _TextSplitter._split_from_maxlenA  s     *-tz/A qgk&7:JKK=q5L "'Y	 \k\?))++)**,,..c)nn$$]** S^^+	

#**,,#i/-?---r5   N)r[   r   )r_   rI   r6   r`   )r6   rn   )rw   rx   rj   r#   r_   rI   r6   r`   )	rU   rV   rW   rX   r4   rm   r   rd   re   r   r5   r3   rG   rG      s         $   #N #N #N #NJ T T T \T(. (. (. (. (. (.r5   rG   c                  Z    e Zd ZdZddZedd	            Zdd
Zedd            Z	ddZ
dS )BasePreChunkeraR  Base-class for per-strategy pre-chunkers.

    The pre-chunker's responsibilities are:

    - **Segregate semantic units.** Identify semantic unit boundaries and segregate elements on
      either side of those boundaries into different sections. In this case, the primary indicator
      of a semantic boundary is a `Title` element. A page-break (change in page-number) is also a
      semantic boundary when `multipage_sections` is `False`.

    - **Minimize chunk count for each semantic unit.** Group the elements within a semantic unit
      into sections as big as possible without exceeding the chunk window size.

    - **Minimize chunks that must be split mid-text.** Precompute the text length of each section
      and only produce a section that exceeds the chunk window size when there is a single element
      with text longer than that window.

    A Table element is placed into a section by itself. CheckBox elements are dropped.

    The "by-title" strategy specifies breaking on section boundaries; a `Title` element indicates
    a new "section", hence the "by-title" designation.
    elementsSequence[Element]r[   r   c                "    || _         || _        d S r+   )	_elementsr]   r2   r   r[   s      r3   r4   zBasePreChunker.__init__  s    !


r5   r6   Iterator[PreChunk]c                >     | ||                                           S )zEGenerate pre-chunks from the element-stream provided on construction.)_iter_pre_chunks)r9   r   r[   s      r3   iter_pre_chunkszBasePreChunker.iter_pre_chunks  s"    
 s8T""33555r5   c              #  ,  K   t          | j                  }| j        D ][}|                     |          s|                    |          s|                                E d{V  |                    |           \|                                E d{V  dS )a  Generate pre-chunks from the element-stream provided on construction.

        A *pre-chunk* is the largest sub-sequence of elements that will both fit within the
        chunking window and respects the semantic boundary rules of the chunking strategy. When a
        single element exceeds the chunking window size it is placed in a pre-chunk by itself and
        is subject to mid-text splitting in the second phase of the chunking process.
        N)PreChunkBuilderr]   r   _is_in_new_semantic_unitwill_fitflushadd_element)r2   pre_chunk_builderelements      r3   r   zBasePreChunker._iter_pre_chunks  s       ,DJ77~ 	3 	3G,,W55 5=N=W=WX_=`=` 5,22444444444 ))'2222 %**,,,,,,,,,,,r5   Tuple[BoundaryPredicate, ...]c                    dS )zBThe semantic-boundary detectors to be applied to break pre-chunks.r   r   r?   s    r3   _boundary_predicatesz#BasePreChunker._boundary_predicates  s	     rr5   r   r   r%   c                H    fd| j         D             }t          |          S )zITrue when `element` begins a new semantic unit such as a section or page.c                &    g | ]} |          S r   r   )rs   predr   s     r3   
<listcomp>z;BasePreChunker._is_in_new_semantic_unit.<locals>.<listcomp>  s!    SSSttG}}SSSr5   )r   any)r2   r   semantic_boundariess    ` r3   r   z'BasePreChunker._is_in_new_semantic_unit  s2    
 TSSS9RSSS&'''r5   N)r   r   r[   r   )r   r   r[   r   r6   r   r6   r   )r6   r   r   r   r6   r%   )rU   rV   rW   rX   r4   rY   r   r   r   r   r   r   r5   r3   r   r   q  s         ,    6 6 6 [6- - - -,    \( ( ( ( ( (r5   r   c                  "    e Zd ZdZddZdd
ZdS )TablePreChunkz/A pre-chunk composed of a single Table element.tabler   r[   r   r6   rO   c                "    || _         || _        d S r+   )_tabler]   )r2   r   r[   s      r3   r4   zTablePreChunk.__init__  s    


r5   Iterator[Table | TableChunk]c              #    K   | j         j        }| j        j        }| j        j        j        pd}| j         j        }t          |          |k    rt          |          |k    r| j        V  dS d}|s|rw ||          \  }}t          |t          j
        | j        j                            }|r |d|         ||d         }}||j        _        |rd|j        _        |V  d}|s|udS dS )zLSplit this pre-chunk into `Table` or `TableChunk` objects maxlen or smaller.rb   NFtextmetadataT)r]   rH   r   r   r   text_as_htmlr@   rc   r   copydeepcopyis_continuation)	r2   rH   text_remainderhtml_remainderrh   r   
chunk_texttable_chunk
chunk_htmls	            r3   iter_chunkszTablePreChunk.iter_chunks  s7     
 )-:@b$ ~&((S-@-@F-J-J+F 	# 	#).~)>)>&J$*t}T[Ma?b?bcccK  ?-;GVG-DnU[U\U\F]N
4>$1  <7;$4"O#  	# 	# 	# 	# 	# 	#r5   N)r   r   r[   r   r6   rO   )r6   r   )rU   rV   rW   rX   r4   r   r   r5   r3   r   r     sB        99   # # # # # #r5   r   c                      e Zd ZdZddZddZd dZd!dZed"d            Z	ed#d            Z
ed$d            Zed%d            Zed&d            Zed'd            ZdS )(TextPreChunkao  A sequence of elements that belong to the same semantic unit within a document.

    The name "section" derives from the idea of a document-section, a heading followed by the
    paragraphs "under" that heading. That structure is not found in all documents and actual section
    content can vary, but that's the concept.

    This object is purposely immutable.
    r   Iterable[Element]r[   r   r6   rO   c                <    t          |          | _        || _        d S r+   )listr   r]   r   s      r3   r4   zTextPreChunk.__init__  s    h


r5   otherr   r%   c                P    t          |t                    sdS | j        |j        k    S )NF)
isinstancer   r   )r2   r   s     r3   __eq__zTextPreChunk.__eq__  s(    %.. 	5~00r5   other_pre_chunkc                H    t          | j        |j        z   | j                  S )zCReturn new `TextPreChunk` that combines this and `other_pre_chunk`.)r[   )r   r   r]   )r2   r   s     r3   combinezTextPreChunk.combine  s!    DN_-FFTZXXXXr5   Iterator[CompositeElement]c              #     K   | j         j        }| j        }| j        }|r% ||          \  }}t	          ||          V  |#dS dS )zSSplit this pre-chunk into one or more `CompositeElement` objects maxlen or smaller.r   N)r]   rH   _consolidated_metadata_textr   )r2   rH   r   rl   r_   s        r3   r   zTextPreChunk.iter_chunks  sq      
 .J	 	> 5++LAy"H======  	> 	> 	> 	> 	>r5   r#   c                *    t          | j                  S )zDLength of concatenated text of this pre-chunk, including separators.)rc   r   r?   s    r3   text_lengthzTextPreChunk.text_length  s     4:r5   Dict[str, List[Any]]c                    dd}t          j        t                    }| j        D ]3} ||j                  D ] \  }}||                             |           !4t          |          S )a  Collection of all populated metadata values across elements.

        The resulting dict has one key for each `ElementMetadata` field that had a non-None value in
        at least one of the elements in this pre-chunk. The value of that key is a list of all those
        populated values, in element order, for example:

            {
                "filename": ["sample.docx", "sample.docx"],
                "languages": [["lat"], ["lat", "eng"]]
                ...
            }

        This preprocessing step provides the input for a specified consolidation strategy that will
        resolve the list of values for each field to a single consolidated value.
        r   r   r6   Iterator[Tuple[str, Any]]c                H    d | j                                         D             S )zM(field_name, value) pair for each non-None field in single `ElementMetadata`.c              3  (   K   | ]\  }}|||fV  d S r+   r   )rs   
field_namevalues      r3   ru   zSTextPreChunk._all_metadata_values.<locals>.iter_populated_fields.<locals>.<genexpr>!  s=        %J$ U#$$$$ r5   )known_fieldsitems)r   s    r3   iter_populated_fieldsz@TextPreChunk._all_metadata_values.<locals>.iter_populated_fields  s2     )1)>)D)D)F)F   r5   )r   r   r6   r   )collectionsdefaultdictr   r   r   appenddict)r2   r   field_valueser   r   s         r3   _all_metadata_valuesz!TextPreChunk._all_metadata_values  s    $	 	 	 	 5@4KD4Q4Q  	7 	7A%:%:1:%F%F 7 7!
EZ(//66667 L!!!r5   r   c                $    t          di | j        S )a  Metadata applicable to this pre-chunk as a single chunk.

        Formed by applying consolidation rules to all metadata fields across the elements of this
        pre-chunk.

        For the sake of consistency, the same rules are applied (for example, for dropping values)
        to a single-element pre-chunk too, even though metadata for such a pre-chunk is already
        "consolidated".
        r   )r   _meta_kwargsr?   s    r3   r   z#TextPreChunk._consolidated_metadata0  s     33!2333r5   Dict[str, List[RegexMetadata]]c                   i }t          | j        j                  }d}d}| j        D ]}t          |j                  }|s||r|ndz  }|}||z  }|j        j        s6t          j        |j        j                  }|	                                D ]Z\  }}	|	D ]"}
|
dxx         |z  cc<   |
dxx         |z  cc<   #|
                    |g           }|                    |	           |||<   [|S )a  Consolidate the regex-metadata in `regex_metadata_dicts` into a single dict.

        This consolidated value is suitable for use in the chunk metadata. `start` and `end`
        offsets of each regex match are also adjusted for their new positions.
        r   startend)rc   r]   rK   r   r   r   regex_metadatar   r   r   getextend)r2   chunk_regex_metadataseparator_lenrunning_text_lenstart_offsetr   text_lenelement_regex_metadata
regex_namematchesmchunk_matchess               r3   _consolidated_regex_metaz%TextPreChunk._consolidated_regex_meta=  sH    @BDJ566~ 	A 	AG7<((H 1A HqH+L(#2  &*]73C3R%S%S"'='C'C'E'E A A#
G  - -AgJJJ,.JJJeHHH,HHHH 4 8 8R H H$$W---3@$Z00A $#r5   Dict[str, Any]c                z     t           t          j                    d fd}t           |                      S )a$  The consolidated metadata values as a dict suitable for constructing ElementMetadata.

        This is where consolidation strategies are actually applied. The output is suitable for use
        in constructing an `ElementMetadata` object like `ElementMetadata(**self._meta_kwargs)`.
        r6   r   c            	   3  
  K   j                                         D ]\  } }                    |           }|j        u r| |d         fV  0|j        u r3| t          |t          t          t                   g                     fV  l|j	        u r2d |D             }| t          |                                          fV  |j        u r| j        fV  |j        u rt          dt!          |            d          dS )zKGenerate (field-name, value) pairs for each field in consolidated metadata.r   c                    i | ]
}|D ]}|d S r+   r   )rs   val_listkeys      r3   
<dictcomp>zGTextPreChunk._meta_kwargs.<locals>.iter_kwarg_pairs.<locals>.<dictcomp>x  s)    *`*`*`W_*`*`PS3*`*`*`*`r5   zmetadata field z& has no defined consolidation strategyN)r   r   r   FIRSTLIST_CONCATENATEsumr   r
   r   LIST_UNIQUEr   keysREGEXr   DROPNotImplementedErrorrepr)r   valuesstrategyordered_unique_keysCSfield_consolidation_strategiesr2   s       r3   iter_kwarg_pairsz3TextPreChunk._meta_kwargs.<locals>.iter_kwarg_pairsl  sN     &*&?&E&E&G&G  "
F9==jIIrx''$fQi/////!444$c&$tCy"2E2E&F&FFFFFF//*`*`V*`*`*`'$d+>+C+C+E+E&F&FFFFFF))$d&CCCCCC(( .b$z*:*:bbb  ' r5   )r6   r   )r   r  r   )r2   r  r  r  s   ` @@r3   r   zTextPreChunk._meta_kwargsb  s]     #)>)])_)_&	 	 	 	 	 	 	 	2 $$&&'''r5   rI   c                b    | j         j        }|                    d | j        D                       S )zThe concatenated text of all elements in this pre-chunk.

        Each element-text is separated from the next by a blank line ("

").
        c              3  2   K   | ]}|j         	|j         V  d S r+   )r   )rs   r   s     r3   ru   z%TextPreChunk._text.<locals>.<genexpr>  s+      "L"LaQV"L16"L"L"L"L"L"Lr5   )r]   rK   joinr   )r2   rK   s     r3   r   zTextPreChunk._text  s3     2"""L"L4>"L"L"LLLLr5   N)r   r   r[   r   r6   rO   )r   r   r6   r%   )r   r   r6   r   )r6   r   rR   )r6   r   )r6   r   )r6   r   )r6   r   rS   )rU   rV   rW   rX   r4   r   r   r   r   r   r   r   r   r   r   r   r5   r3   r   r     s;           1 1 1 1
Y Y Y Y	> 	> 	> 	>    \
  "  "  " \ "D 
4 
4 
4 \
4 "$ "$ "$ \"$H "( "( "( \"(H M M M \M M Mr5   r   c                  j    e Zd ZdZddZdd	ZddZddZedd            Z	ddZ
edd            ZdS )r   a  An element accumulator suitable for incrementally forming a pre-chunk.

    Provides the trial method `.will_fit()` a pre-chunker can use to determine whether it should add
    the next element in the element stream.

    `.flush()` is used to build a PreChunk object from the accumulated elements. This method
    returns an iterator that generates zero-or-one `TextPreChunk` or `TablePreChunk` object and is
    used like so:

        yield from builder.flush()

    If no elements have been accumulated, no `PreChunk` instance is generated. Flushing the builder
    clears the elements it contains so it is ready to build the next pre-chunk.
    r[   r   r6   rO   c                p    || _         t          |j                  | _        g | _        g | _        d| _        d S )Nr   )r]   rc   rK   _separator_lenr   _text_segments	_text_lenr^   s     r3   r4   zPreChunkBuilder.__init__  s8    
!$"566(* *,r5   r   r   c                    | j                             |           |j        rC| j                            |j                   | xj        t          |j                  z  c_        dS dS )zAdd `element` to this section.N)r   r   r   r  r  rc   r2   r   s     r3   r   zPreChunkBuilder.add_element  sc    g&&&< 	0&&w|444NNc',///NNNN	0 	0r5   r   c              #    K   | j         sdS t          | j         d         t                    r t          | j         d         | j                  n&t          t          | j                   | j                  }|                                  |V  dS )a  Generate zero-or-one `PreChunk` object and clear the accumulator.

        Suitable for use to emit a PreChunk when the maximum size has been reached or a semantic
        boundary has been reached. Also to clear out a terminal pre-chunk at the end of an element
        stream.
        Nr   )r   r   r   r   r]   r   r   _reset_stater2   	pre_chunks     r3   r   zPreChunkBuilder.flush  s       ~ 	F $.+U33@M$.+TZ888 d4>22DJ??	 	 	r5   r%   c                &   t          | j                  dk    rdS t          |t                    rdS | j        r"t          | j        d         t                    rdS | j        | j        j        k    rdS | j        t          |j                  k     rdS dS )aU  True when `element` can be added to this prechunk without violating its limits.

        There are several limits:
        - A `Table` element will never fit with any other element. It will only fit in an empty
          pre-chunk.
        - No element will fit in a pre-chunk that already contains a `Table` element.
        - A text-element will not fit in a pre-chunk that already exceeds the soft-max
          (aka. new_after_n_chars).
        - A text-element will not fit when together with the elements already present it would
          exceed the hard-max (aka. max_characters).
        r   TF)	rc   r   r   r   _text_lengthr]   r<   _remaining_spacer   r  s     r3   r   zPreChunkBuilder.will_fit  s     t~!##4gu%% 	5> 	j):EBB 	5tz2225 3w|#4#4445tr5   r#   c                h    | j         t          | j                  z  }| j        j        | j        z
  |z
  S )zMMaximum text-length of an element that can be added without exceeding maxlen.)r  rc   r  r]   r@   r  )r2   separators_lens     r3   r  z PreChunkBuilder._remaining_space  s3     ,s43F/G/GGz"T^3nDDr5   c                x    | j                                          | j                                         d| _        dS )zMSet working-state values back to "empty", ready to accumulate next pre-chunk.r   N)r   clearr  r  r?   s    r3   r  zPreChunkBuilder._reset_state  s6    !!###r5   c                `    t          | j                  }|r|dz
  nd}| j        || j        z  z   S )a  Length of the text in this pre-chunk.

        This value represents the chunk-size that would result if this pre-chunk was flushed in its
        current state. In particular, it does not include the length of a trailing separator (since
        that would only appear if an additional element was added).

        Not suitable for judging remaining space, use `.remaining_space` for that value.
        rz   r   )rc   r  r  r  )r2   nseparator_counts      r3   r  zPreChunkBuilder._text_length  s;     #$$#$+!a%%!~43F!FGGr5   Nr[   r   r6   rO   )r   r   r6   rO   r   r   rR   rT   )rU   rV   rW   rX   r4   r   r   r   propertyr  r  r  r   r5   r3   r   r     s                0 0 0 0   *   : E E E XE    H H H XH H Hr5   r   c                  "    e Zd ZdZddZdd	Zd
S )PreChunkCombinerzDFilters pre-chunk stream to combine small pre-chunks where possible.
pre_chunksIterable[PreChunk]r[   r   c                "    || _         || _        d S r+   )_pre_chunksr]   )r2   r#  r[   s      r3   r4   zPreChunkCombiner.__init__  s    %


r5   r6   r   c              #    K   t          | j                  }| j        j        }| j        D ]{}t	          |t
                    s|j        |k    s|j        |j        k     r|                                E d{V  t	          |t
                    r|V  f|	                    |           ||                                E d{V  dS )zVGenerate pre-chunk objects, combining TextPreChunk objects when they'll fit in window.N)
TextPreChunkAccumulatorr]   r    r&  r   r   r   remaining_spacer   add_pre_chunk)r2   accumr    r  s       r3   iter_combined_pre_chunksz)PreChunkCombiner.iter_combined_pre_chunks  s      '
33%)Z%J") 	/ 	/I 9m44) $(BBB(9+@@@ ;;==((((((( )]33 /##I....;;==         r5   N)r#  r$  r[   r   r   )rU   rV   rW   rX   r4   r,  r   r5   r3   r"  r"    sB        NN   ! ! ! ! ! !r5   r"  c                  Z    e Zd ZdZddZdd	ZddZedd            Zedd            Z	dS )r(  a  Accumulates, measures, and combines text pre-chunks.

    Used for combining pre-chunks for chunking strategies like "by-title" that can potentially
    produce undersized chunks and offer the `combine_text_under_n_chars` option. Note that only
    sequential `TextPreChunk` objects can be combined. A `TablePreChunk` is never combined with
    another pre-chunk.

    Provides `.add_pre_chunk()` allowing a pre-chunk to be added to the chunk and provides
    monitoring properties `.remaining_space` and `.text_length` suitable for deciding whether to add
    another pre-chunk.

    `.flush()` is used to combine the accumulated pre-chunks into a single `TextPreChunk` object.
    This method returns an interator that generates zero-or-one `TextPreChunk` objects and is used
    like so:

        yield from accum.flush()

    If no pre-chunks have been accumulated, no `TextPreChunk` is generated. Flushing the builder
    clears the pre-chunks it contains so it is ready to accept the next text-pre-chunk.
    r[   r   r6   rO   c                "    || _         g | _        d S r+   )r]   r&  r^   s     r3   r4   z TextPreChunkAccumulator.__init__B  s    
/1r5   r  r   c                :    | j                             |           dS )zPAdd a pre-chunk to the accumulator for possible combination with next pre-chunk.N)r&  r   r  s     r3   r*  z%TextPreChunkAccumulator.add_pre_chunkF  s    	*****r5   Iterator[TextPreChunk]c              #     K   | j         }|sdS |d         }|dd         D ]}|                    |          }|V  |                                 dS )zCGenerate all accumulated pre-chunks as a single combined pre-chunk.Nr   rz   )r&  r   r  )r2   r#  r  r   s       r3   r   zTextPreChunkAccumulator.flushJ  sw      %
  	F qM	)!""~ 	; 	;O!))/::II 	r5   r#   c                t    | j         j        }| j        s|n#|| j        z
  t	          | j         j                  z
  S )zEMaximum size of pre-chunk that can be added without exceeding maxlen.)r]   r@   r&  r   rc   rK   )r2   rh   s     r3   r)  z'TextPreChunkAccumulator.remaining_space[  sD     $ #LFF $**S1J-K-KK		
r5   c                    t          | j                  }|dk    rdS t          d | j        D                       }t          | j        j                  |dz
  z  }||z   S )z;Size of concatenated text in all pre-chunks in accumulator.r   c              3  $   K   | ]}|j         V  d S r+   )r   )rs   r_   s     r3   ru   z6TextPreChunkAccumulator.text_length.<locals>.<genexpr>n  s$      HH!HHHHHHr5   rz   )rc   r&  r   r]   rK   )r2   r  total_text_lengthtotal_separator_lengths       r3   r   z#TextPreChunkAccumulator.text_lengthf  sj      !!661HHt7GHHHHH!$TZ%>!?!?1q5!I #999r5   Nr  )r  r   r6   rO   )r6   r0  rR   )
rU   rV   rW   rX   r4   r*  r   r   r)  r   r   r5   r3   r(  r(  ,  s         *2 2 2 2+ + + +   " 
 
 
 X
 	: 	: 	: X	: 	: 	:r5   r(  r6   c                      dddfd} | S )	a  Not a predicate itself, calling this returns a predicate that triggers on each new section.

    The lifetime of the returned callable cannot extend beyond a single element-stream because it
    stores current state (current section) that is particular to that element stream.

    A "section" of this type is particular to the EPUB format (so far) and not to be confused with
    a "section" composed of a section-heading (`Title` element) followed by content elements.

    The returned predicate tracks the current section, starting at `None`. Calling with an element
    with a different value for `metadata.section` returns True, indicating the element starts a new
    section boundary, and updates the enclosed section name ready for the next transition.
    NTr   r   r6   r%   c                L    | j         j        }r|ddS |dS |k    rdS |dS )NFT)r   section)r   r9  current_sectionis_firsts     r3   section_changedz+is_in_next_section.<locals>.section_changed  sQ     "*
  	%OH5
 ?5 o%%5!tr5   r   r   )r<  r:  r;  s    @@r3   is_in_next_sectionr=    s;     &*OH      4 r5   c                      dddfd} | S )	a  Not a predicate itself, calling this returns a predicate that triggers on each new page.

    The lifetime of the returned callable cannot extend beyond a single element-stream because it
    stores current state (current page-number) that is particular to that element stream.

    The returned predicate tracks the "current" page-number, starting at 1. An element with a
    greater page number returns True, indicating the element starts a new page boundary, and
    updates the enclosed page-number ready for the next transition.

    An element with `page_number == None` or a page-number lower than the stored value is ignored
    and returns False.
    rz   Tr   r   r6   r%   c                P    | j         j        }r|pdddS |dS |k    rdS |dS )Nrz   FT)r   page_number)r   r@  current_page_numberr;  s     r3   page_number_incrementedz0is_on_next_page.<locals>.page_number_incremented  sZ     &2
  	"-"2H5
 5---5 *tr5   r   r   )rB  rA  r;  s    @@r3   is_on_next_pagerC    s<      !H      6 #"r5   r   r   r%   c                ,    t          | t                    S )z:True when `element` is a `Title` element, False otherwise.)r   r   )r   s    r3   is_titlerE    s    gu%%%r5   )r6   r   r   )/rX   
__future__r   r   r   typingr   r   r   r   r   r	   r
   r   r   r   r   rq   typing_extensionsr   r   unstructured.documents.elementsr   r   r   r   r   r   r   r   unstructured.utilsr   r%   r   __annotations__r   r   rG   r   r   r   r   r"  r(  r=  rC  rE  r   r5   r3   <module>rL     s^   F F F " " " " " "                                - - - - - - - -	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 , + + + + +'	48  8 8 8 8 X4 4 4 4 4 3Ee Ee Ee Ee Ee Ee Ee EePq. q. q. q. q. q. q. q.rC( C( C( C( C( C( C( C(V&# &# &# &# &# &# &# &#RiM iM iM iM iM iM iM iMhoH oH oH oH oH oH oH oHd! ! ! ! ! ! ! !BD: D: D: D: D: D: D: D:t* * * *Z+# +# +# +#\& & & & & &r5   