o
    a,                     @   s   d Z ddlZddlmZ ddlmZ ddlmZ ddlm	Z	 ddl
mZmZmZmZmZ d	d
gZdZdZeeZeZdd ZG dd deZdd Zdd Zdd Zdd Z	d!ddZ		d"dd	Zdd Zd d
 Z dS )#a  Converts cubic bezier curves to quadratic splines.

Conversion is performed such that the quadratic splines keep the same end-curve
tangents as the original cubics. The approach is iterative, increasing the
number of segments for a spline until the error gets below a bound.

Respective curves from multiple fonts will be converted at once to ensure that
the resulting splines are interpolation-compatible.
    N)AbstractPen)PointToSegmentPen)ReverseContourPen   )curves_to_quadratic)UnequalZipLengthsErrorIncompatibleSegmentNumberErrorIncompatibleSegmentTypesErrorIncompatibleGlyphsErrorIncompatibleFontsErrorfonts_to_quadraticfont_to_quadraticgMbP?z&com.github.googlei18n.cu2qu.curve_typec                  G   s.   t tdd | D dkrt|  tt|  S )zyEnsure each argument to zip has the same length. Also make sure a list is
    returned for python 2/3 compatibility.
    c                 s   s    | ]}t |V  qd S Nlen).0a r   5/usr/lib/python3/dist-packages/fontTools/cu2qu/ufo.py	<genexpr>7       zzip.<locals>.<genexpr>r   )r   setr   list_zip)argsr   r   r   zip2   s   r   c                   @   sX   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d Zdd Z	dd Z
dd Zdd ZdS )GetSegmentsPenzPen to collect segments into lists of points for conversion.

    Curves always include their initial on-curve point, so some points are
    duplicated between segments.
    c                 C   s   d | _ g | _d S r   )_last_ptsegmentsselfr   r   r   __init__C   s   
zGetSegmentsPen.__init__c                 G   s&   |dv r	|d | _ | j||f d S )N)movelineqcurvecurve)r   r   append)r    tagr   r   r   r   _add_segmentG   s   
zGetSegmentsPen._add_segmentc                 C      |  d| d S )Nr"   r)   r    ptr   r   r   moveToL      zGetSegmentsPen.moveToc                 C   r*   )Nr#   r+   r,   r   r   r   lineToO   r/   zGetSegmentsPen.lineToc                 G      | j d| jg|R   d S )Nr$   r)   r   r    Zpointsr   r   r   qCurveToR      zGetSegmentsPen.qCurveToc                 G   r1   )Nr%   r2   r3   r   r   r   curveToU   r5   zGetSegmentsPen.curveToc                 C      |  d d S )Ncloser+   r   r   r   r   	closePathX      zGetSegmentsPen.closePathc                 C   r7   )Nendr+   r   r   r   r   endPath[   r:   zGetSegmentsPen.endPathc                 C   s   d S r   r   )r    Z	glyphNameZtransformationr   r   r   addComponent^   s   zGetSegmentsPen.addComponentN)__name__
__module____qualname____doc__r!   r)   r.   r0   r4   r6   r9   r<   r=   r   r   r   r   r   <   s    r   c                 C   s"   t  }t|dd}| | |jS )z6Get a glyph's segments as extracted by GetSegmentsPen.T)ZoutputImpliedClosingLine)r   r   Z
drawPointsr   )glyphpenZpointPenr   r   r   _get_segmentsb   s   
rD   c                 C   s   |    |  }|rt|}|D ]K\}}|dkr|j|  q|dkr(|j|  q|dkr6|j|dd   q|dkrD|j|dd   q|dkrM|  q|dkrV|  qt	d	| dS )
z=Draw segments as extracted by GetSegmentsPen back to a glyph.r"   r#   r%   r   Nr$   r8   r;   zUnhandled segment type "%s")
ZclearContoursZgetPenr   r.   r0   r6   r4   r9   r<   AssertionError)rB   r   reverse_directionrC   r(   r   r   r   r   _set_segmentsu   s&   

rG   c                    s   t dd | D sJ dtdd | D |}t|d  t  fdd|dd	 D s0J d
t d }||dd ||< dd |D S )z2Return quadratic approximations of cubic segments.c                 s   s    | ]	}|d  dkV  qdS )r   r%   Nr   r   sr   r   r   r          z)_segments_to_quadratic.<locals>.<genexpr>zNon-cubic given to convertc                 S      g | ]}|d  qS )r   r   rH   r   r   r   
<listcomp>       z*_segments_to_quadratic.<locals>.<listcomp>r   c                 3   s    | ]	}t | kV  qd S r   r   rH   nr   r   r      rJ   r   NzConverted incompatibly   c                 S   s   g | ]}d |fqS )r$   r   )r   pr   r   r   rL      rM   )allr   r   strget)r   max_errstatsZ
new_pointsZspline_lengthr   rN   r   _segments_to_quadratic   s   &rW   c                    s   zt dd | D  }W n ty   t| w t|sdS |}g }i }t|D ]4\}}	|	d d  t fdd|	dd D sJd	d |	D ||< n d
krVt|	||}	d}||	 q'|rrt | }
t | |
D ]
\}}t||| qg|rzt	| |d|S )zDo the actual conversion of a set of compatible glyphs, after arguments
    have been set up.

    Return True if the glyphs were modified, else return False.
    c                 S   s   g | ]}t |qS r   )rD   )r   gr   r   r   rL      rM   z(_glyphs_to_quadratic.<locals>.<listcomp>Fr   c                 3   s    | ]	}|d   kV  qdS )r   Nr   rH   r(   r   r   r      rJ   z'_glyphs_to_quadratic.<locals>.<genexpr>r   Nc                 S   rK   )r   r   rH   r   r   r   rL      rM   r%   T)r   )
r   r   r   any	enumeraterR   rW   r'   rG   r	   )glyphsrU   rF   rV   Zsegments_by_locationZglyphs_modifiedZnew_segments_by_locationZincompatibleir   Znew_segments_by_glyphrB   Znew_segmentsr   rY   r   _glyphs_to_quadratic   s2   r^   Fc                 C   s\   |du ri }|st d }t|ttfr|}n|gt|  }t|t| ks'J t| |||S )a  Convert the curves of a set of compatible of glyphs to quadratic.

    All curves will be converted to quadratic at once, ensuring interpolation
    compatibility. If this is not required, calling glyphs_to_quadratic with one
    glyph at a time may yield slightly more optimized results.

    Return True if glyphs were modified, else return False.

    Raises IncompatibleGlyphsError if glyphs have non-interpolatable outlines.
    Ni  )DEFAULT_MAX_ERR
isinstancer   tupler   r^   )r\   rU   rF   rV   
max_errorsr   r   r   glyphs_to_quadratic   s   rc   Tc                    sF  |r4dd | D }t |dkr)tt|}|dkr td dS |dkr%nt|t |dkr4td d	u r:i  rB|rBtd
 sH|sHt t	|t
tfr\t |t | ksYJ |}	n	|re|gt |  }	t	 t
tfrt | t  ksvJ dd t|  D }	n r fdd| D }	d}
i }t jdd | D  D ]F}g }g }t| |	D ]\}}||v r|||  || qz|
t|||O }
W q ty } zt| |||< W Y d	}~qd	}~ww |rt||
r|rt }tddfdd|D   |r!| D ]}|jtd}|dkrd|jt< d}
q
|
S )a  Convert the curves of a collection of fonts to quadratic.

    All curves will be converted to quadratic at once, ensuring interpolation
    compatibility. If this is not required, calling fonts_to_quadratic with one
    font at a time may yield slightly more optimized results.

    Return True if fonts were modified, else return False.

    By default, cu2qu stores the curve type in the fonts' lib, under a private
    key "com.github.googlei18n.cu2qu.curve_type", and will not try to convert
    them again if the curve type is already set to "quadratic".
    Setting 'remember_curve_type' to False disables this optimization.

    Raises IncompatibleFontsError if same-named glyphs from different fonts
    have non-interpolatable outlines.
    c                 S   s   h | ]	}|j td qS )cubic)librT   CURVE_TYPE_LIB_KEYr   fr   r   r   	<setcomp>   s    z%fonts_to_quadratic.<locals>.<setcomp>r   Z	quadraticz%Curves already converted to quadraticFrd   z'fonts may contain different curve typesNz4Only one of max_err and max_err_em can be specified.c                 S   s   g | ]
\}}|j j| qS r   infoZ
unitsPerEm)r   rh   er   r   r   rL     s    z&fonts_to_quadratic.<locals>.<listcomp>c                    s   g | ]}|j j  qS r   rj   rg   )
max_err_emr   r   rL     s    c                 s   s    | ]}|  V  qd S r   )keysrg   r   r   r   r     r   z%fonts_to_quadratic.<locals>.<genexpr>zNew spline lengths: %sz, c                 3   s     | ]}d | | f V  qdS )z%s: %dNr   )r   l)rV   r   r   r   +  s    
T)r   nextiterloggerrk   NotImplementedErrorZwarning	TypeErrorr_   r`   r   ra   r   r   unionr'   r^   r
   errorr   sortedrn   joinre   rT   rf   )Zfontsrm   rU   rF   rV   Z
dump_statsZremember_curve_typeZcurve_typesZ
curve_typerb   ZmodifiedZglyph_errorsnamer\   Zcur_max_errorsfontrv   excZspline_lengthsr   )rm   rV   r   r      s   






c                 K      t | gfi |S )zConvenience wrapper around glyphs_to_quadratic, for just one glyph.
    Return True if the glyph was modified, else return False.
    )rc   )rB   kwargsr   r   r   glyph_to_quadratic7     r~   c                 K   r|   )zConvenience wrapper around fonts_to_quadratic, for just one font.
    Return True if the font was modified, else return False.
    )r   )rz   r}   r   r   r   r   ?  r   )NFN)NNFNFT)!rA   ZloggingZfontTools.pens.basePenr   ZfontTools.pens.pointPenr   Z fontTools.pens.reverseContourPenr    r   errorsr   r   r	   r
   r   __all__r_   rf   Z	getLoggerr>   rr   r   r   r   rD   rG   rW   r^   rc   r   r~   r   r   r   r   r   <module>   s2   


&'

Y