Fix: DP algorithm now excludes leading/trailing space box widths to match rendering behavior
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
This commit is contained in:
parent
836ff75c89
commit
c60ddb3903
90
ekp.el
90
ekp.el
@ -175,31 +175,6 @@ Each takes ideal, stretch (+), and shrink (-) values."
|
|||||||
"[A-Za-z'\\-\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u024F\u1E00-\u1EFF]"
|
"[A-Za-z'\\-\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u024F\u1E00-\u1EFF]"
|
||||||
"Regexp matching Latin characters including accented forms.")
|
"Regexp matching Latin characters including accented forms.")
|
||||||
|
|
||||||
(defun ekp--split-hyphenated-word (word)
|
|
||||||
"Split WORD at existing hyphens into parts with hyphens attached.
|
|
||||||
E.g., \"help-echo\" -> (\"help-\" \"echo\").
|
|
||||||
Handles edge cases: trailing hyphens, leading hyphens, consecutive hyphens.
|
|
||||||
Does NOT apply dictionary hyphenation - just uses existing hyphens."
|
|
||||||
(let ((result nil)
|
|
||||||
(current nil) ; list of chars (built in reverse)
|
|
||||||
(i 0)
|
|
||||||
(len (length word)))
|
|
||||||
(while (< i len)
|
|
||||||
(let ((char (aref word i)))
|
|
||||||
(if (= char ?-)
|
|
||||||
;; Hyphen: attach to current segment and push
|
|
||||||
(progn
|
|
||||||
(push char current)
|
|
||||||
(push (apply #'string (nreverse current)) result)
|
|
||||||
(setq current nil))
|
|
||||||
;; Non-hyphen: accumulate
|
|
||||||
(push char current)))
|
|
||||||
(cl-incf i))
|
|
||||||
;; Push remaining content (last segment after final hyphen, or entire word if no hyphen)
|
|
||||||
(unless (null current)
|
|
||||||
(push (apply #'string (nreverse current)) result))
|
|
||||||
(nreverse result)))
|
|
||||||
|
|
||||||
(defun ekp--split-with-hyphen (string)
|
(defun ekp--split-with-hyphen (string)
|
||||||
"Split STRING into boxes with hyphenation points marked.
|
"Split STRING into boxes with hyphenation points marked.
|
||||||
Returns (boxes-vector . hyphen-positions-vector)."
|
Returns (boxes-vector . hyphen-positions-vector)."
|
||||||
@ -210,16 +185,12 @@ Returns (boxes-vector . hyphen-positions-vector)."
|
|||||||
(format "^\\([[{<„‚¿¡*@\"']*\\)\\(%s+\\)\\([]}>.,*?\"']*\\)$"
|
(format "^\\([[{<„‚¿¡*@\"']*\\)\\(%s+\\)\\([]}>.,*?\"']*\\)$"
|
||||||
ekp--latin-regexp)
|
ekp--latin-regexp)
|
||||||
box)
|
box)
|
||||||
;; Latin word (possibly with embedded hyphens)
|
;; Latin word: apply hyphenation
|
||||||
(let* ((left (match-string 1 box))
|
(let* ((left (match-string 1 box))
|
||||||
(word (match-string 2 box))
|
(word (match-string 2 box))
|
||||||
(right (match-string 3 box))
|
(right (match-string 3 box))
|
||||||
;; If word contains hyphens, split at those points only (no further hyphenation)
|
(parts (ekp-hyphen-boxes
|
||||||
;; Otherwise, apply dictionary hyphenation
|
(ekp-hyphen-create ekp-latin-lang) word))
|
||||||
(parts (if (string-match-p "-" word)
|
|
||||||
(ekp--split-hyphenated-word word)
|
|
||||||
(ekp-hyphen-boxes
|
|
||||||
(ekp-hyphen-create ekp-latin-lang) word)))
|
|
||||||
(n (length parts)))
|
(n (length parts)))
|
||||||
(when left (setcar parts (concat left (car parts))))
|
(when left (setcar parts (concat left (car parts))))
|
||||||
(when right (setcar (last parts)
|
(when right (setcar (last parts)
|
||||||
@ -613,16 +584,51 @@ When looseness != 0, alt-paths tracks alternative paths by (position . line-coun
|
|||||||
(list backptrs demerits rests gaps
|
(list backptrs demerits rests gaps
|
||||||
hyphen-counts fitness-classes line-counts alt-paths)))
|
hyphen-counts fitness-classes line-counts alt-paths)))
|
||||||
|
|
||||||
|
(defun ekp--leading-space-width (i boxes-types boxes-widths)
|
||||||
|
"Compute total width of leading space boxes starting at position I.
|
||||||
|
Returns 0 if box at I is not a space box."
|
||||||
|
(let ((n (length boxes-types))
|
||||||
|
(width 0)
|
||||||
|
(pos i))
|
||||||
|
(while (and (< pos n)
|
||||||
|
(let ((box-type (aref boxes-types pos)))
|
||||||
|
(and box-type (eq (car box-type) 'space))))
|
||||||
|
(cl-incf width (aref boxes-widths pos))
|
||||||
|
(cl-incf pos))
|
||||||
|
width))
|
||||||
|
|
||||||
|
(defun ekp--trailing-space-width (k boxes-types boxes-widths)
|
||||||
|
"Compute total width of trailing space boxes ending before position K.
|
||||||
|
K is the exclusive end position (break point).
|
||||||
|
Returns 0 if box at K-1 is not a space box."
|
||||||
|
(let ((width 0)
|
||||||
|
(pos (1- k)))
|
||||||
|
(while (and (>= pos 0)
|
||||||
|
(let ((box-type (aref boxes-types pos)))
|
||||||
|
(and box-type (eq (car box-type) 'space))))
|
||||||
|
(cl-incf width (aref boxes-widths pos))
|
||||||
|
(cl-decf pos))
|
||||||
|
width))
|
||||||
|
|
||||||
(defun ekp--dp-line-metrics (para i k glues-types ideal-prefixs min-prefixs max-prefixs)
|
(defun ekp--dp-line-metrics (para i k glues-types ideal-prefixs min-prefixs max-prefixs)
|
||||||
"Compute line metrics for boxes I to K using PARA's stored glue params.
|
"Compute line metrics for boxes I to K using PARA's stored glue params.
|
||||||
Returns (ideal-pixel min-pixel max-pixel) excluding leading glue."
|
Returns (ideal-pixel min-pixel max-pixel) excluding leading glue and leading/trailing space boxes."
|
||||||
(let ((leading-glue-type (aref glues-types i)))
|
(let* ((leading-glue-type (aref glues-types i))
|
||||||
|
(boxes-types (ekp-para-boxes-types para))
|
||||||
|
(boxes-widths (ekp-para-boxes-widths para))
|
||||||
|
;; Compute width of leading/trailing space boxes (these will be stripped during rendering)
|
||||||
|
(leading-space-width (ekp--leading-space-width i boxes-types boxes-widths))
|
||||||
|
(trailing-space-width (ekp--trailing-space-width k boxes-types boxes-widths))
|
||||||
|
(space-width (+ leading-space-width trailing-space-width)))
|
||||||
(list (- (aref ideal-prefixs k) (aref ideal-prefixs i)
|
(list (- (aref ideal-prefixs k) (aref ideal-prefixs i)
|
||||||
(ekp--para-glue-ideal para leading-glue-type))
|
(ekp--para-glue-ideal para leading-glue-type)
|
||||||
|
space-width)
|
||||||
(- (aref min-prefixs k) (aref min-prefixs i)
|
(- (aref min-prefixs k) (aref min-prefixs i)
|
||||||
(ekp--para-glue-min para leading-glue-type))
|
(ekp--para-glue-min para leading-glue-type)
|
||||||
|
space-width)
|
||||||
(- (aref max-prefixs k) (aref max-prefixs i)
|
(- (aref max-prefixs k) (aref max-prefixs i)
|
||||||
(ekp--para-glue-max para leading-glue-type)))))
|
(ekp--para-glue-max para leading-glue-type)
|
||||||
|
space-width))))
|
||||||
|
|
||||||
(defun ekp--dp-force-break (para i k arrays glues-types hyphen-positions ideal-prefixs hyphen-pixel line-pixel)
|
(defun ekp--dp-force-break (para i k arrays glues-types hyphen-positions ideal-prefixs hyphen-pixel line-pixel)
|
||||||
"Force a break at K-1 when no valid break found. Update ARRAYS.
|
"Force a break at K-1 when no valid break found. Update ARRAYS.
|
||||||
@ -635,9 +641,17 @@ Uses PARA's stored glue params for consistency."
|
|||||||
(line-counts (nth 6 arrays))
|
(line-counts (nth 6 arrays))
|
||||||
(break-pos (1- k))
|
(break-pos (1- k))
|
||||||
(hyphenate-p (ekp--hyphenate-p hyphen-positions break-pos))
|
(hyphenate-p (ekp--hyphenate-p hyphen-positions break-pos))
|
||||||
|
(boxes-types (ekp-para-boxes-types para))
|
||||||
|
(boxes-widths (ekp-para-boxes-widths para))
|
||||||
|
;; Exclude leading/trailing space boxes (will be stripped during rendering)
|
||||||
|
;; Use k as the exclusive end for trailing space calculation
|
||||||
|
(leading-space-width (ekp--leading-space-width i boxes-types boxes-widths))
|
||||||
|
(trailing-space-width (ekp--trailing-space-width k boxes-types boxes-widths))
|
||||||
|
(space-width (+ leading-space-width trailing-space-width))
|
||||||
(ideal-pixel (- (aref ideal-prefixs break-pos)
|
(ideal-pixel (- (aref ideal-prefixs break-pos)
|
||||||
(aref ideal-prefixs i)
|
(aref ideal-prefixs i)
|
||||||
(ekp--para-glue-ideal para (aref glues-types i))))
|
(ekp--para-glue-ideal para (aref glues-types i))
|
||||||
|
space-width))
|
||||||
(rest-pixel (- line-pixel ideal-pixel)))
|
(rest-pixel (- line-pixel ideal-pixel)))
|
||||||
(when hyphenate-p (cl-incf ideal-pixel hyphen-pixel))
|
(when hyphenate-p (cl-incf ideal-pixel hyphen-pixel))
|
||||||
;; Force break with high demerits
|
;; Force break with high demerits
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user