
    kh[3                         d Z ddlZddlmZ ddlmZ  G d d      Z G d d	e      Z G d
 de      Z G d de      Z	 G d de      Z
 G d d      Z G d d      Zy)u  
Objects representing PDF path (stroke and filling) extracted from pdf drawings and annotations.

Data structure based on results of ``page.get_drawings()``::

    {
        'color': (x,x,x) or None,  # stroke color
        'fill' : (x,x,x) or None,  # fill color
        'width': float,            # line width
        'closePath': bool,         # whether to connect last and first point
        'rect' : rect,             # page area covered by this path
        'items': [                 # list of draw commands: lines, rectangle or curves.
            ("l", p1, p2),         # a line from p1 to p2
            ("c", p1, p2, p3, p4), # cubic Bézier curve from p1 to p4, p2 and p3 are the control points
            ("re", rect),          # a rect represented with two diagonal points
            ("qu", quad)           # a quad represented with four corner points
        ],
        ...
    }

References: 
    - https://pymupdf.readthedocs.io/en/latest/page.html#Page.get_drawings
    - https://pymupdf.readthedocs.io/en/latest/faq.html#extracting-drawings

.. note::
    The coordinates extracted by ``page.get_drawings()`` is based on **real** page CS, i.e. with rotation 
    considered. This is different from ``page.get_text('rawdict')``.
    N   )	rgb_value)	constantsc                   &    e Zd ZdZd ZdedefdZy)Segmentz9A segment of path, e.g. a line or a rectangle or a curve.c                     |dd  | _         y N   points)selfitems     O/var/www/teggl/fontify/venv/lib/python3.12/site-packages/pdf2docx/shape/Path.py__init__zSegment.__init__'   s    12h    widthcolorc                     g S N )r   r   r   s      r   
to_strokeszSegment.to_strokes*   s    "9r   N__name__
__module____qualname____doc__r   floatlistr   r   r   r   r   r   %   s    C =u<D<r   r   c                   0    e Zd ZdZed        ZdedefdZy)Lz(Line path with source ``("l", p1, p2)``.c                 r    | j                   d   \  }}| j                   d   \  }}||z
  dz  ||z
  dz  z   dz  S )Nr   r
   r   g      ?r   )r   x0y0x1y1s        r   lengthzL.length0   sD    QBQBB
BrEA:%++r   r   r   c                 |    g }|j                  | j                  d   | j                  d   |t        |      d       |S )a  Convert to stroke dict.

        Args:
            width (float): Specify width for the stroke.
            color (list): Specify color for the stroke.

        Returns:
            list: A list of ``Stroke`` dicts. 
        
        .. note::
            A line corresponds to one stroke, but considering the consistence, 
            the return stroke dict is append to a list. So, the length of list 
            is always 1.
        r   r
   startendr   r   )appendr   r   )r   r   r   strokess       r   r   zL.to_strokes7   sB     QQ"5)	 	 r   N)	r   r   r   r   propertyr&   r   r   r   r   r   r   r    r    -   s*    2, ,u D r   r    c                   &    e Zd ZdZd ZdedefdZy)Rz'Rect path with source ``("re", rect)``.c                 D    |d   \  }}}}||f||f||f||f||fg| _         y r	   r   )r   r   r"   r#   r$   r%   s         r   r   z
R.__init__R   s=     aBBHr2hR2r(RHr   r   r   c           	          g }t        t        | j                        dz
        D ]A  }|j                  | j                  |   | j                  |dz      |dz  t	        |      d       C |S )aJ  Convert each edge to stroke dict.

        Args:
            width (float): Specify width for the stroke.
            color (list): Specify color for the stroke.

        Returns:
            list: A list of ``Stroke`` dicts. 
        
        .. note::
            One Rect path is converted to a list of 4 stroke dicts.
        r
          @r(   )rangelenr   r+   r   )r   r   r   r,   is        r   r   zR.to_strokes[   sm     s4;;')* 	ANN![[^![[1-"S[&u-	 	 r   Nr   r   r   r   r/   r/   P   s    1u D r   r/   c                       e Zd ZdZd Zy)Qz'Quad path with source ``("qu", quad)``.c                 0    |d   \  }}}}|||||g| _         y r	   r   )r   r   ulurlllrs         r   r   z
Q.__init__u   s'     aBB2r2r*r   N)r   r   r   r   r   r   r   r   r7   r7   s   s
    1+r   r7   c                       e Zd ZdZy)Cz8Bezier curve path with source ``("c", p1, p2, p3, p4)``.N)r   r   r   r   r   r   r   r>   r>   |   s    Br   r>   c                       e Zd ZdZddefdZd Zed        Zed        Z	ed        Z
ed        Zd	ed
efdZd
efdZy)Segmentsz,A sub-path composed of one or more segments.itemsc                 l   g | _         |D ]  }|d   dk(  r%| j                   j                  t        |             0|d   dk(  r%| j                   j                  t        |             ]|d   dk(  r%| j                   j                  t	        |             |d   dk(  s| j                   j                  t        |              |rpd| j                   d   j                  d   | j                   d   j                  d   f}t        |      }|j                  dkD  r| j                   j                  |       y y y )Nr   lcrequMbP?)
_instancesr+   r    r>   r/   r7   r   r&   )r   rA   
close_pathr   lines        r   r   zSegments.__init__   s    	BDaC$//"8"84"AaC$//"8"84"AaD$//"8"84"AaD$//"8"84"A		B ,33B79K9R9RST9UVDT7D{{4!7!7!= r   c                 (    d | j                   D        S )Nc              3       K   | ]  }|  y wr   r   ).0instances     r   	<genexpr>z$Segments.__iter__.<locals>.<genexpr>   s     IXIs   )rI   r   s    r   __iter__zSegments.__iter__   s    IIIr   c                 b    g }| j                   D ]  }|j                  |j                          |S )zConnected points of segments.)rI   extendr   )r   r   segments      r   r   zSegments.points   s1      	*GMM'..)	*r   c                     | j                   j                         }|dk(  xs  | j                  |z  t        j                  k\  S )zCISO-oriented criterion: the ratio of real area to bbox exceeds 0.9.r   )bboxget_areaarear   FACTOR_MOST)r   	bbox_areas     r   is_iso_orientedzSegments.is_iso_oriented   s9     II&&(	!|Ityy2I4I4IIIr   c                 "   | j                   }|d   |d   }}t        |d   |d   z
        t        |d   |d   z
        z   dkD  ryd}t        t        |      dz
        D ]#  }||   \  }}||dz      \  }}	|||	z  ||z  z
  z  }% t        |dz        S )zCalculate segments area with Green formulas. Note the boundary of Bezier curve 
        is simplified with its control points.
        
        * https://en.wikipedia.org/wiki/Shoelace_formula
        r   rG   r
   rH           r2   )r   absr3   r4   )
r   r   r)   r*   rY   r5   r"   r#   r$   r%   s
             r   rY   zSegments.area   s     AYr
suQxAE!HSVO 44T9 s6{1}% 	"AAYFBAaC[FBBrEBrEM!D	"
 48}r   c           	      "   | j                   }t        |d       d   }t        |d       d   }t        |d       d   }t        |d       d   }t        j                  t        |d      t        |d      t        |d      t        |d            S )	zCalculate segments bbox. c                     | d   S Nr   r   points    r   <lambda>zSegments.bbox.<locals>.<lambda>   
    58 r   )keyr   c                     | d   S r	   r   rc   s    r   re   zSegments.bbox.<locals>.<lambda>   rf   r   r
   c                     | d   S rb   r   rc   s    r   re   zSegments.bbox.<locals>.<lambda>   rf   r   c                     | d   S r	   r   rc   s    r   re   zSegments.bbox.<locals>.<lambda>   rf   r   r   )r   minmaxfitzRectround)r   r   r"   r#   r$   r%   s         r   rW   zSegments.bbox   s     34Q734Q734Q734Q7 yy"aL%A,b!eBlD 	Dr   r   r   c                 n    g }| j                   D ]#  }|j                  |j                  ||             % |S )zConvert each segment to a ``Stroke`` dict.

        Args:
            width (float): Specify stroke width.
            color (list): Specify stroke color.

        Returns:
            list: A list of ``Stroke`` dicts.
        )rI   rT   r   )r   r   r   r,   rU   s        r   r   zSegments.to_strokes   s:      	=GNN7--eU;<	=r   c                 D    t        | j                        t        |      dS )zConvert segment closed area to a ``Fill`` dict.

        Args:
            color (list): Specify fill color.

        Returns:
            dict: ``Fill`` dict.
        )rW   r   )r   rW   r   )r   r   s     r   to_fillzSegments.to_fill   s!     $))_u%
 	
r   N)F)r   r   r   r   r   r   rR   r-   r   r\   rY   rW   r   r   rr   r   r   r   r@   r@      s    6>T > J   J J  * 
D 
Du D  
D 
r   r@   c                       e Zd ZdZdefdZed        Zed        Z	ed        Z
ed        Zd Zd	ed
efdZd
efdZd Zy)Pathz=Path extracted from PDF, consist of one or more ``Segments``.rawc                    || _         |d   | _        | j                  rdn|j                  dd      }g | _        t        j                         | _        |j                  dd      }| j                  |d         D ]h  }t        ||      }| j                  j                  |       |j                  }|j                         dk(  r|| | ||fz  }| xj                  |z  c_        j y	)
zInit path in real page CS.

        Args:
            raw (dict): Raw dict extracted with `PyMuPDF`, see link
            https://pymupdf.readthedocs.io/en/latest/page.html#Page.get_drawings
        typeT	closePathFr   r^   rA   r   N)ru   	path_typeis_fillgetrA   rm   rn   rW   _group_segmentsr@   r+   rX   )r   ru   rJ   wsegmentsSrects          r   r   zPath.__init__   s     V "\\Tsww{E/J
 
IIK	GGGS!,,S\: 	H:.AJJa  66D}}!4QBAq>#94III	r   c                 &   g g }}d}| D ]r  }|d   dv r:|d   |d   }}|r||k(  r|j                  |       n|j                  |       |g}|}D|d   dv sL|r|j                  |       g }|j                  |g       t |r|j                  |       |S )zGroup connected segments.

        Args:
            items (dict): Raw dict extracted from ``page.get_drawings()``.

        Returns:
            list: A list of segments list.
        Nr   )rC   rD   r
   rG   )rE   rF   )r+   )rA   r~   segments_listcursorr   r)   r*   s          r   r|   zPath._group_segments  s     #%b- 	-DAw*$!!Wd2hs  5&=OOD) "((2 $vH  aL(!((2!H$$dV,3	-8 ]))(3r   c                     d| j                   v S )Nsry   rQ   s    r   	is_strokezPath.is_stroke3  s     #t~~ 55r   c                     d| j                   v S )Nfr   rQ   s    r   rz   zPath.is_fill6  s    !T^^33r   c                 B    | j                   D ]  }|j                  r y y)z@It is iso-oriented when all contained segments are iso-oriented.FT)rA   r\   )r   r~   s     r   r\   zPath.is_iso_oriented9  s%     

 	:H++E	:r   c                 d   g }| j                   rY| j                  j                  dd      }| j                  j                  dd      }|j                  | j	                  ||             | j
                  r<| j                  j                  dd      }|j                  | j                  |             |S )zkConvert path to ``Shape`` raw dicts.

        Returns:
            list: A list of ``Shape`` dict.
        r   Nr   r^   fill)r   ru   r{   rT   _to_strokesrz   	_to_fills)r   
iso_shapesstroke_colorr   
fill_colors        r   	to_shapeszPath.to_shapesA  s     
 >>88<<6LHHLL#.Ed..ulCD <<fd3JdnnZ89r   r   r   c                 n    g }| j                   D ]#  }|j                  |j                  ||             % |S )zmConvert path to ``Stroke`` raw dicts.

        Returns:
            list: A list of ``Stroke`` dict.
        )rA   rT   r   )r   r   r   r,   r~   s        r   r   zPath._to_strokesW  s:     

 	>HNN8..ue<=	>r   c                 l    g }| j                   D ]"  }|j                  |j                  |             $ |S )zConvert path to ``Fill`` raw dicts.

        Returns:
            list: A list of ``Fill`` dict.
        
        .. note::
            The real filling area of this path may be not a rectangle.        
        )rA   r+   rr   )r   r   fillsr~   s       r   r   zPath._to_fillsc  s8     

 	2HLL))%01	2r   c                    | j                   j                  dg       D ]  }|d   dk(  r|j                  |d   |d          $|d   dk(  r|j                  |d          A|d   dk(  r|j	                  |d          ^|d   dk(  r!|j                  |d   |d   |d	   |d
          t        d|       |j                  | j                   j                  dd      | j                   j                  dd      | j                   j                  dd      | j                   j                  dd      | j                   j                  dd      | j                   j                  dd      d| j                   v rt        | j                   d         nd| j                   j                  dd      | j                   j                  dd      | j                   j                  dd      
       y)z Plot path for debug purpose.

        Args:
            canvas: ``PyMuPDF`` drawing canvas by ``page.new_shape()``.

        Reference:
        
            https://pymupdf.readthedocs.io/en/latest/faq.html#extracting-drawings
        rA   r   rC   r
   r   rE   rF   rD         zunhandled drawingr   Nr   dasheseven_oddFrx   lineJoinlineCapr   stroke_opacityfill_opacity)
r   r   r   r   rx   r   r   r   r   r   )	ru   r{   	draw_line	draw_rect	draw_quaddraw_bezier
ValueErrorfinishrl   )r   canvasr   s      r   plotz	Path.plotr  s    HHLL"- 
	<DAw#~  a$q'2aD  a)aD  a)aC""47DGT!Wd1gF !4d;;
	< 	fd+((,,w-88<<$/XX\\*e4hhll;6XX\\*a009TXX0EC+,1((,,w*88<<(8!<na8 	 	r   N)r   r   r   r   dictr   staticmethodr|   r-   r   rz   r\   r   r   r   r   r   r   r   r   r   rt   rt      s    G4 8 ) )X 5 53 3 ,	 	T 	d #r   rt   )r   rm   common.sharer   commonr   r   r    r/   r7   r>   r@   rt   r   r   r   <module>r      sj   :  $ = =   F   F+ +	 	
d
 d
Nm mr   