--- a/src/reportlab/graphics/charts/textlabels.py Thu Feb 11 14:24:59 2021 +0000
+++ b/src/reportlab/graphics/charts/textlabels.py Fri Feb 19 10:50:39 2021 +0000
@@ -5,106 +5,29 @@
import string
from reportlab.lib import colors
-from reportlab.lib.utils import simpleSplit, _simpleSplit, isBytes
+from reportlab.lib.utils import simpleSplit, _simpleSplit
from reportlab.lib.validators import isNumber, isNumberOrNone, OneOf, isColorOrNone, isString, \
isTextAnchor, isBoxAnchor, isBoolean, NoneOr, isInstanceOf, isNoneOrString, isNoneOrCallable, \
isSubclassOf
from reportlab.lib.attrmap import *
-from reportlab.pdfbase.pdfmetrics import stringWidth, getAscentDescent, getFont, unicode2T1
+from reportlab.pdfbase.pdfmetrics import stringWidth, getAscentDescent, getFont
from reportlab.graphics.shapes import Drawing, Group, Circle, Rect, String, STATE_DEFAULTS
-from reportlab.graphics.shapes import _PATH_OP_ARG_COUNT, _PATH_OP_NAMES, definePath
from reportlab.graphics.widgetbase import Widget, PropHolder
from reportlab.graphics.shapes import _baseGFontName, DirectDraw
from reportlab.platypus import XPreformatted, Paragraph, Flowable
from reportlab.lib.styles import ParagraphStyle, PropertySet
from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER
_ta2al = dict(start=TA_LEFT,end=TA_RIGHT,middle=TA_CENTER)
+from ..utils import (text2Path as _text2Path, #here for continuity
+ pathNumTrunc as _pathNumTrunc,
+ processGlyph as _processGlyph,
+ text2PathDescription as _text2PathDescription)
_A2BA= {
'x': {0:'n', 45:'ne', 90:'e', 135:'se', 180:'s', 225:'sw', 270:'w', 315: 'nw', -45: 'nw'},
'y': {0:'e', 45:'se', 90:'s', 135:'sw', 180:'w', 225:'nw', 270:'n', 315: 'ne', -45: 'ne'},
}
-def _pathNumTrunc(n):
- if int(n)==n: return int(n)
- return round(n,5)
-
-def _processGlyph(G, truncate=1, pathReverse=0):
- O = []
- P = []
- R_append = [].append
- if G and len(G)==1 and G[0][0]=='lineTo':
- G = (('moveToClosed',)+G[0][1:],)+G #hack fix for some errors
- for g in G+(('end',),):
- op = g[0]
- if O and op in ['moveTo', 'moveToClosed','end']:
- if O[0]=='moveToClosed':
- del O[0]
- if pathReverse:
- P[1::2],P[0::2] = P[0::2],P[1::2] #exchange x and y
- P.reverse()
- O.reverse()
- O.insert(0,'moveTo')
- O.append('closePath')
- i = 0
- if truncate: P = list(map(_pathNumTrunc,P))
- for o in O:
- j = i + _PATH_OP_ARG_COUNT[_PATH_OP_NAMES.index(o)]
- if o=='closePath':
- R_append(o)
- else:
- R_append((o,)+ tuple(P[i:j]))
- i = j
- O = []
- P = []
- O.append(op)
- P.extend(g[1:])
- return R_append.__self__
-
-def _text2PathDescription(text, x=0, y=0, fontName=_baseGFontName, fontSize=1000,
- anchor='start', truncate=1, pathReverse=0):
- from reportlab.graphics import renderPM, _renderPM
- font = getFont(fontName)
- if font._multiByte and not font._dynamicFont:
- raise ValueError("_text2PathDescription doesn't support multi byte fonts like %r" % fontName)
- P_extend = [].extend
- if not anchor=='start':
- textLen = stringWidth(text, fontName, fontSize)
- if anchor=='end':
- x = x-textLen
- elif anchor=='middle':
- x = x - textLen/2.
- _gs = _renderPM.gstate(1,1)
- _setFont = renderPM._setFont
- _setFont(_gs,fontName,fontSize)
- if font._dynamicFont:
- for g in _gs._stringPath(text,x,y):
- P_extend(_processGlyph(g,truncate=truncate,pathReverse=pathReverse))
- else:
- if isBytes(text):
- try:
- text = text.decode('utf8')
- except UnicodeDecodeError as e:
- i,j = e.args[2:4]
- raise UnicodeDecodeError(*(e.args[:4]+('%s\n%s-->%s<--%s' % (e.args[4],text[max(i-10,0):i],text[i:j],text[j:j+10]),)))
- fc = font
- FT = unicode2T1(text,[font]+font.substitutionFonts)
- nm1 = len(FT)-1
- for i, (f, t) in enumerate(FT):
- if f!=fc:
- _setFont(_gs,f.fontName,fontSize)
- fc = f
- for g in _gs._stringPath(t,x,y):
- P_extend(_processGlyph(g,truncate=truncate,pathReverse=pathReverse))
- if i!=nm1:
- x += f.stringWidth(t.decode(f.encName), fontSize)
- return P_extend.__self__
-
-def _text2Path(text, x=0, y=0, fontName=_baseGFontName, fontSize=1000,
- anchor='start', truncate=1, pathReverse=0,**kwds):
- return definePath(_text2PathDescription(text,x=x,y=y,fontName=fontName,
- fontSize=fontSize,anchor=anchor,truncate=truncate,pathReverse=pathReverse),**kwds)
-
try:
from rlextra.graphics.canvasadapter import DirectDrawFlowable
except ImportError:
--- a/src/reportlab/graphics/renderPM.py Thu Feb 11 14:24:59 2021 +0000
+++ b/src/reportlab/graphics/renderPM.py Fri Feb 19 10:50:39 2021 +0000
@@ -18,9 +18,7 @@
from math import sin, cos, pi, ceil
from reportlab.lib.utils import getStringIO, getBytesIO, open_and_read, isUnicode
from reportlab import rl_config, ascii
-
-class RenderPMError(Exception):
- pass
+from .utils import setFont as _setFont, RenderPMError
import os, sys
@@ -243,20 +241,6 @@
c.pathFill(fillMode)
c.pathStroke()
-def _setFont(gs,fontName,fontSize):
- try:
- gs.setFont(fontName,fontSize)
- except ValueError as e:
- if not e.args[0].endswith("Can't find font!"): raise
- #here's where we try to add a font to the canvas
- try:
- f = getFont(fontName)
- _renderPM.makeT1Font(fontName,f.face.findT1File(),f.encoding.vector,open_and_read)
- except:
- s1, s2 = list(map(str,sys.exc_info()[:2]))
- raise RenderPMError("Can't setFont(%s) missing the T1 files?\nOriginally %s: %s" % (fontName,s1,s2))
- gs.setFont(fontName,fontSize)
-
def _convert2pilp(im):
Image = _getImage()
return im.convert("P", dither=Image.NONE, palette=Image.ADAPTIVE)
--- a/src/reportlab/graphics/renderbase.py Thu Feb 11 14:24:59 2021 +0000
+++ b/src/reportlab/graphics/renderbase.py Fri Feb 19 10:50:39 2021 +0000
@@ -1,4 +1,4 @@
-#Copyright ReportLab Europe Ltd. 2000-2017
+#Copyright ReportLab Europe Ltd. 2000-2021
#see license.txt for license details
#history https://hg.reportlab.com/hg-public/reportlab/log/tip/src/reportlab/graphics/renderbase.py
@@ -9,26 +9,7 @@
from reportlab.lib.validators import DerivedValue
from reportlab import rl_config
-def inverse(A):
- "For A affine 2D represented as 6vec return 6vec version of A**(-1)"
- # I checked this RGB
- det = float(A[0]*A[3] - A[2]*A[1])
- R = [A[3]/det, -A[1]/det, -A[2]/det, A[0]/det]
- return tuple(R+[-R[0]*A[4]-R[2]*A[5],-R[1]*A[4]-R[3]*A[5]])
-
-def mmult(A, B):
- "A postmultiplied by B"
- # I checked this RGB
- # [a0 a2 a4] [b0 b2 b4]
- # [a1 a3 a5] * [b1 b3 b5]
- # [ 1 ] [ 1 ]
- #
- return (A[0]*B[0] + A[2]*B[1],
- A[1]*B[0] + A[3]*B[1],
- A[0]*B[2] + A[2]*B[3],
- A[1]*B[2] + A[3]*B[3],
- A[0]*B[4] + A[2]*B[5] + A[4],
- A[1]*B[4] + A[3]*B[5] + A[5])
+from . transform import mmult, inverse
def getStateDelta(shape):
"""Used to compute when we need to change the graphics state.
--- a/src/reportlab/graphics/shapes.py Thu Feb 11 14:24:59 2021 +0000
+++ b/src/reportlab/graphics/shapes.py Fri Feb 19 10:50:39 2021 +0000
@@ -6,7 +6,7 @@
__doc__='''Core of the graphics library - defines Drawing and Shapes'''
import os, sys
-from math import pi, cos, sin, tan, sqrt, radians, floor
+from math import pi, cos, sin, sqrt, radians, floor
from pprint import pprint
from reportlab.platypus import Flowable
@@ -65,68 +65,9 @@
}
####################################################################
-# math utilities. These could probably be moved into lib
-# somewhere.
+# math utilities. These are now in reportlab.graphics.transform
####################################################################
-
-# constructors for matrices:
-def nullTransform():
- return (1, 0, 0, 1, 0, 0)
-
-def translate(dx, dy):
- return (1, 0, 0, 1, dx, dy)
-
-def scale(sx, sy):
- return (sx, 0, 0, sy, 0, 0)
-
-def rotate(angle):
- a = angle * pi/180
- return (cos(a), sin(a), -sin(a), cos(a), 0, 0)
-
-def skewX(angle):
- a = angle * pi/180
- return (1, 0, tan(a), 1, 0, 0)
-
-def skewY(angle):
- a = angle * pi/180
- return (1, tan(a), 0, 1, 0, 0)
-
-def mmult(A, B):
- "A postmultiplied by B"
- # I checked this RGB
- # [a0 a2 a4] [b0 b2 b4]
- # [a1 a3 a5] * [b1 b3 b5]
- # [ 1 ] [ 1 ]
- #
- return (A[0]*B[0] + A[2]*B[1],
- A[1]*B[0] + A[3]*B[1],
- A[0]*B[2] + A[2]*B[3],
- A[1]*B[2] + A[3]*B[3],
- A[0]*B[4] + A[2]*B[5] + A[4],
- A[1]*B[4] + A[3]*B[5] + A[5])
-
-def inverse(A):
- "For A affine 2D represented as 6vec return 6vec version of A**(-1)"
- # I checked this RGB
- det = float(A[0]*A[3] - A[2]*A[1])
- R = [A[3]/det, -A[1]/det, -A[2]/det, A[0]/det]
- return tuple(R+[-R[0]*A[4]-R[2]*A[5],-R[1]*A[4]-R[3]*A[5]])
-
-def zTransformPoint(A,v):
- "Apply the homogenous part of atransformation a to vector v --> A*v"
- return (A[0]*v[0]+A[2]*v[1],A[1]*v[0]+A[3]*v[1])
-
-def transformPoint(A,v):
- "Apply transformation a to vector v --> A*v"
- return (A[0]*v[0]+A[2]*v[1]+A[4],A[1]*v[0]+A[3]*v[1]+A[5])
-
-def transformPoints(matrix, V):
- r = [transformPoint(matrix,v) for v in V]
- if isinstance(V,tuple): r = tuple(r)
- return r
-
-def zTransformPoints(matrix, V):
- return list(map(lambda x,matrix=matrix: zTransformPoint(matrix,x), V))
+from . transform import *
def _textBoxLimits(text, font, fontSize, leading, textAnchor, boxAnchor):
w = 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/reportlab/graphics/transform.py Fri Feb 19 10:50:39 2021 +0000
@@ -0,0 +1,75 @@
+'''functions for 2D affine transformations'''
+__all__ = (
+ 'nullTransform',
+ 'translate',
+ 'scale',
+ 'rotate',
+ 'skewX',
+ 'skewY',
+ 'mmult',
+ 'inverse',
+ 'zTransformPoint',
+ 'transformPoint',
+ 'transformPoints',
+ 'zTransformPoints',
+ )
+from math import pi, cos, sin, tan
+
+# constructors for matrices:
+def nullTransform():
+ return (1, 0, 0, 1, 0, 0)
+
+def translate(dx, dy):
+ return (1, 0, 0, 1, dx, dy)
+
+def scale(sx, sy):
+ return (sx, 0, 0, sy, 0, 0)
+
+def rotate(angle):
+ a = angle * pi/180
+ return (cos(a), sin(a), -sin(a), cos(a), 0, 0)
+
+def skewX(angle):
+ a = angle * pi/180
+ return (1, 0, tan(a), 1, 0, 0)
+
+def skewY(angle):
+ a = angle * pi/180
+ return (1, tan(a), 0, 1, 0, 0)
+
+def mmult(A, B):
+ "A postmultiplied by B"
+ # I checked this RGB
+ # [a0 a2 a4] [b0 b2 b4]
+ # [a1 a3 a5] * [b1 b3 b5]
+ # [ 1 ] [ 1 ]
+ #
+ return (A[0]*B[0] + A[2]*B[1],
+ A[1]*B[0] + A[3]*B[1],
+ A[0]*B[2] + A[2]*B[3],
+ A[1]*B[2] + A[3]*B[3],
+ A[0]*B[4] + A[2]*B[5] + A[4],
+ A[1]*B[4] + A[3]*B[5] + A[5])
+
+def inverse(A):
+ "For A affine 2D represented as 6vec return 6vec version of A**(-1)"
+ # I checked this RGB
+ det = float(A[0]*A[3] - A[2]*A[1])
+ R = [A[3]/det, -A[1]/det, -A[2]/det, A[0]/det]
+ return tuple(R+[-R[0]*A[4]-R[2]*A[5],-R[1]*A[4]-R[3]*A[5]])
+
+def zTransformPoint(A,v):
+ "Apply the homogenous part of atransformation a to vector v --> A*v"
+ return (A[0]*v[0]+A[2]*v[1],A[1]*v[0]+A[3]*v[1])
+
+def transformPoint(A,v):
+ "Apply transformation a to vector v --> A*v"
+ return (A[0]*v[0]+A[2]*v[1]+A[4],A[1]*v[0]+A[3]*v[1]+A[5])
+
+def transformPoints(matrix, V):
+ r = [transformPoint(matrix,v) for v in V]
+ if isinstance(V,tuple): r = tuple(r)
+ return r
+
+def zTransformPoints(matrix, V):
+ return list(map(lambda x,matrix=matrix: zTransformPoint(matrix,x), V))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/reportlab/graphics/utils.py Fri Feb 19 10:50:39 2021 +0000
@@ -0,0 +1,110 @@
+__all__ = (
+ 'setFont',
+ 'pathNumTrunc',
+ 'processGlyph',
+ 'text2PathDescription',
+ 'text2Path',
+ 'RenderPMError',
+ )
+from . _renderPM import makeT1Font
+from reportlab.pdfbase.pdfmetrics import getFont, unicode2T1
+from reportlab.lib.utils import open_and_read, isBytes
+from .shapes import _baseGFontName, _PATH_OP_ARG_COUNT, _PATH_OP_NAMES, definePath
+from sys import exc_info
+
+class RenderPMError(Exception):
+ pass
+
+def setFont(gs,fontName,fontSize):
+ try:
+ gs.setFont(fontName,fontSize)
+ except ValueError as e:
+ if not e.args[0].endswith("Can't find font!"): raise
+ #here's where we try to add a font to the canvas
+ try:
+ f = getFont(fontName)
+ makeT1Font(fontName,f.face.findT1File(),f.encoding.vector,open_and_read)
+ except:
+ s1, s2 = list(map(str,exc_info()[:2]))
+ raise RenderPMError("Can't setFont(%s) missing the T1 files?\nOriginally %s: %s" % (fontName,s1,s2))
+ gs.setFont(fontName,fontSize)
+
+def pathNumTrunc(n):
+ if int(n)==n: return int(n)
+ return round(n,5)
+
+def processGlyph(G, truncate=1, pathReverse=0):
+ O = []
+ P = []
+ R_append = [].append
+ if G and len(G)==1 and G[0][0]=='lineTo':
+ G = (('moveToClosed',)+G[0][1:],)+G #hack fix for some errors
+ for g in G+(('end',),):
+ op = g[0]
+ if O and op in ['moveTo', 'moveToClosed','end']:
+ if O[0]=='moveToClosed':
+ del O[0]
+ if pathReverse:
+ P[1::2],P[0::2] = P[0::2],P[1::2] #exchange x and y
+ P.reverse()
+ O.reverse()
+ O.insert(0,'moveTo')
+ O.append('closePath')
+ i = 0
+ if truncate: P = list(map(pathNumTrunc,P))
+ for o in O:
+ j = i + _PATH_OP_ARG_COUNT[_PATH_OP_NAMES.index(o)]
+ if o=='closePath':
+ R_append(o)
+ else:
+ R_append((o,)+ tuple(P[i:j]))
+ i = j
+ O = []
+ P = []
+ O.append(op)
+ P.extend(g[1:])
+ return R_append.__self__
+
+def text2PathDescription(text, x=0, y=0, fontName=_baseGFontName, fontSize=1000,
+ anchor='start', truncate=1, pathReverse=0, gs=None):
+ font = getFont(fontName)
+ if font._multiByte and not font._dynamicFont:
+ raise ValueError("text2PathDescription doesn't support multi byte fonts like %r" % fontName)
+ P_extend = [].extend
+ if not anchor=='start':
+ textLen = stringWidth(text, fontName, fontSize)
+ if anchor=='end':
+ x = x-textLen
+ elif anchor=='middle':
+ x = x - textLen/2.
+ if gs is None:
+ from ._renderPM import gstate
+ gs = gstate(1,1)
+ setFont(gs,fontName,fontSize)
+ if font._dynamicFont:
+ for g in gs._stringPath(text,x,y):
+ P_extend(processGlyph(g,truncate=truncate,pathReverse=pathReverse))
+ else:
+ if isBytes(text):
+ try:
+ text = text.decode('utf8')
+ except UnicodeDecodeError as e:
+ i,j = e.args[2:4]
+ raise UnicodeDecodeError(*(e.args[:4]+('%s\n%s-->%s<--%s' % (e.args[4],text[max(i-10,0):i],text[i:j],text[j:j+10]),)))
+ fc = font
+ FT = unicode2T1(text,[font]+font.substitutionFonts)
+ nm1 = len(FT)-1
+ for i, (f, t) in enumerate(FT):
+ if f!=fc:
+ setFont(gs,f.fontName,fontSize)
+ fc = f
+ for g in gs._stringPath(t,x,y):
+ P_extend(processGlyph(g,truncate=truncate,pathReverse=pathReverse))
+ if i!=nm1:
+ x += f.stringWidth(t.decode(f.encName), fontSize)
+ return P_extend.__self__
+
+def text2Path(text, x=0, y=0, fontName=_baseGFontName, fontSize=1000,
+ anchor='start', truncate=1, pathReverse=0, gs=None, **kwds):
+ return definePath(text2PathDescription(text,x=x,y=y,fontName=fontName,
+ fontSize=fontSize,anchor=anchor,truncate=truncate,pathReverse=pathReverse, gs=gs),**kwds)