improvments to embedded soft hyphenation; version --> 3.5.62
authorrobin
Wed, 03 Mar 2021 21:19:20 +0000
changeset 4648 75aa496d5cc7
parent 4647 c3ea8ad241c8
child 4649 f094d273903a
improvments to embedded soft hyphenation; version --> 3.5.62
CHANGES.md
setup.py
src/reportlab/__init__.py
src/reportlab/platypus/paragraph.py
tests/test_platypus_paragraphs.py
--- a/CHANGES.md	Sun Feb 28 18:30:30 2021 +0000
+++ b/CHANGES.md	Wed Mar 03 21:19:20 2021 +0000
@@ -11,7 +11,12 @@
 The contributors lists are in no order and apologies to those accidentally not
 mentioned. If we missed you, please let us know!
 
-CHANGES  3.5.61	 dd/02/2021
+CHANGES  3.5.62	 03/03/2021
+---------------------------
+	* simplify annotateException and add better error messages for asUnicode/Bytes etc
+	* improve embeddedHyphenation in paragraph.py
+
+CHANGES  3.5.61	 25/02/2021
 ---------------------------
 	* add adjustableArrow widget
 	* allow para tag borderPadding attribute
--- a/setup.py	Sun Feb 28 18:30:30 2021 +0000
+++ b/setup.py	Wed Mar 03 21:19:20 2021 +0000
@@ -1,6 +1,6 @@
 #Copyright ReportLab Europe Ltd. 2000-2017
 #see license.txt for license details
-__version__='3.5.56'
+__version__='3.5.62'
 import os, sys, glob, shutil, re
 def specialOption(n):
     v = False
--- a/src/reportlab/__init__.py	Sun Feb 28 18:30:30 2021 +0000
+++ b/src/reportlab/__init__.py	Wed Mar 03 21:19:20 2021 +0000
@@ -1,9 +1,9 @@
-#Copyright ReportLab Europe Ltd. 2000-2018
+#Copyright ReportLab Europe Ltd. 2000-2021
 #see license.txt for license details
 __doc__="""The Reportlab PDF generation library."""
-Version = "3.5.61"
+Version = "3.5.62"
 __version__=Version
-__date__='20210225'
+__date__='20210303'
 
 import sys, os
 
--- a/src/reportlab/platypus/paragraph.py	Sun Feb 28 18:30:30 2021 +0000
+++ b/src/reportlab/platypus/paragraph.py	Wed Mar 03 21:19:20 2021 +0000
@@ -575,10 +575,12 @@
     '''a fragword containing soft hyphens some of its strings are _SHYIndexedStr'''
     def shyphenate(self, newWidth, maxWidth):
         ww = self[0]
+        self._fsww = 0x7fffffff
         if ww==0: return []
         possible = None
         exceeded = False
         baseWidth = baseWidth0 = newWidth - ww
+        fsww = None
         for i,(f,t) in enumerate(self[1:]):
             sW = lambda s: stringWidth(s, f.fontName, f.fontSize)
             if isinstance(t,_SHYIndexedStr):
@@ -588,6 +590,7 @@
                 for j, x in enumerate(t._shyIndices):
                     left, right = t[:x], t[x:]
                     leftw = bw+sW(left)
+                    if fsww is None: fsww = leftw
                     exceeded = leftw > maxWidth
                     if exceeded: break
                     possible = i, j, x, leftw, left, right, shyLen
@@ -595,7 +598,8 @@
             else:
                 baseWidth += sW(t)
                 exceeded = baseWidth > maxWidth
-            if exceeded: break
+            if exceeded and fsww is not None: break
+        self._fsww = fsww-baseWidth0 if fsww is not None else 0x7fffffff
         if not possible: return []
         i, j, x, leftw, left, right, shyLen = possible
         i1 = i+1
@@ -1182,9 +1186,14 @@
             baseWidth = currentWidth + spaceWidth + hyphenWidth
             limWidth = maxWidth + spaceShrink
             '''
+            self._fsww = 0x7fffffff
             for i, sp in reversed(list(enumerate(self.__sp__))):
+                #we iterate backwards so that we return the longest that fits
+                #else we will end up with the shortest value in self._fsww
                 sw = self[:sp]
-                swnw = baseWidth + stringWidth(sw, fontName, fontSize, encoding)
+                sww = stringWidth(sw, fontName, fontSize, encoding)
+                if not i: self._fsww = sww
+                swnw = baseWidth + sww
                 if swnw <= limWidth:
                     #we found a suitable split in a soft-hyphenated word
                     T = self.__sp__[i:] + [len(self)]
@@ -2079,17 +2088,20 @@
                             self._hyphenations += 1
                             forcedSplit = 1
                             continue
-                        elif hyphenation2 and len(cLine):
-                            hsw = word.__shysplit__(
-                                fontName, fontSize,
-                                0 + hyw - 1e-8,
-                                maxWidth,
-                                encoding = self.encoding,
-                                )
-                            if hsw:
-                                words[0:0] = [word]
-                                forcedSplit = 1
-                                word = None
+                        elif len(cLine):
+                            nMW = maxWidths[min(maxlineno,lineno)]
+                            if hyphenation2 or (word._fsww+hyw+1e-8)<=nMW:
+                                hsw = word.__shysplit__(
+                                    fontName, fontSize,
+                                    0 + hyw - 1e-8,
+                                    nMW,
+                                    encoding = self.encoding,
+                                    )
+                                if hsw:
+                                    words[0:0] = [word]
+                                    forcedSplit = 1
+                                    word = None
+                                    newWidth = currentWidth
                     elif attemptHyphenation:
                         hyOk = not getattr(f,'nobr',False)
                         hsw = _hyphenateWord(hyphenator if hyOk else None,
@@ -2109,6 +2121,7 @@
                             if hsw:
                                 words[0:0] = [word]
                                 forcedSplit = 1
+                                newWidth = currentWidth
                                 word = None
                     if splitLongWords and not (isinstance(word,_SplitWord) or forcedSplit):
                         nmw = min(lineno,maxlineno)
@@ -2191,12 +2204,14 @@
                             FW.pop(-1)  #remove this as we are doing this one again
                             self._hyphenations += 1
                             continue
-                        elif hyphenation2 and len(FW)>1:    #only if we are not the first word on the line
-                            hsw = w.shyphenate(wordWidth, maxWidth)
-                            if hsw:
-                                _words[0:0] = [_InjectedFrag([0,(f.clone(_fkind=_FK_BREAK,text=''),'')]),w]
-                                FW.pop(-1)  #remove this as we are doing this one again
-                                continue
+                        elif len(FW)>1: #only if we are not the first word on the line
+                            nMW = maxWidths[min(maxlineno,lineno)]  #next maxWidth or current one
+                            if hyphenation2 or w._fsww+1e-8<=nMW:
+                                hsw = w.shyphenate(wordWidth, nMW)
+                                if hsw:
+                                    _words[0:0] = [_InjectedFrag([0,(f.clone(_fkind=_FK_BREAK,text=''),'')]),w]
+                                    FW.pop(-1)  #remove this as we are doing this one again
+                                    continue
                         #else: try to split an overlong word
                     elif attemptHyphenation:
                         hyOk = not getattr(f,'nobr',False)
--- a/tests/test_platypus_paragraphs.py	Sun Feb 28 18:30:30 2021 +0000
+++ b/tests/test_platypus_paragraphs.py	Wed Mar 03 21:19:20 2021 +0000
@@ -217,12 +217,12 @@
         c = Canvas(outputfile('test_platypus_paragraphs_embedded2.pdf'), pagesize=pagesize)
 
         expected = [
-                (163, b'\x7f\xd7_^\x07\x9c!u\x02\xb4\x13z\xb5y6C'),
-                (163, b'\xab\x82\xbf\xb3\xfc\xb5\xaau\x15\xb1<\x8eX\xbe35'),
-                (163, b'LL\xb1\xfd.\x8a\x8bk\xde\xcdK\x16P\xebWh'),
-                (163, b'\xa9fj\xdd\xb9\x11N\xcf\xbb\x12\xfb\xd9\xa5\xf0v<'),
-                (163, b'\x18r\x98\x17\xd8&\x8c\x01{\xf3|r\xca\xccQy'),
-                (163, b'\xb7\xf2\x0f/d\xe9<\xd1B?\xe5\x8c\xcbO\x06?'),
+                (163, b':\n\x16\x8c\xa9\x87\xf4C\xe0\xe6\xfd/\x07\xe7\xde8'),
+                (163, b'4\x98_\t\xd0\xde\x9a:8\xa7E\xfc\x13\xad\xfbk'),
+                (163, b'9m\xec\xdfX\xe4\x85\xe9tL,\xe5ob\x13w'),
+                (163, b"\xf39*6\x85\xd2\xf9`:<\xe8'\xa9\x8a\xec["),
+                (163, b'\x85\x07\x83\xee\x8d\x11Bp\xd0p\xaa\xcbp\x98\xf4\xb7'),
+                (163, b'\xf0"wp\x91vN\xc8<\xef\xe7\xc8s\xae\xf8\x10'),
                 ]
         observed = []
         for eh in 0, 1, 2: