Fix glue parameter caching inconsistency for multi-font paragraphs
When multiple paragraphs with different fonts were processed, subsequent executions would use wrong glue parameters from the cache. This caused misalignment because different fonts have different spacing values. Changes: - Add glue-params field to ekp-para struct to store params at creation - Add helper functions: ekp--para-glue-ideal/shrink/stretch/min/max - Update all C module interface functions to use para's stored params - Update all glue computation functions for consistency Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
This commit is contained in:
parent
fee05f769d
commit
333618bb57
141
ekp.el
141
ekp.el
@ -86,6 +86,8 @@ Set to nil to force pure Elisp implementation.")
|
||||
boxes boxes-widths boxes-types glues-types
|
||||
hyphen-pixel hyphen-positions
|
||||
ideal-prefixs min-prefixs max-prefixs
|
||||
;; Store glue params at para creation time for consistent C module calls
|
||||
glue-params ; plist (:lws-ideal :lws-shrink :lws-stretch :mws-* :cws-*)
|
||||
(dp-cache nil :type hash-table))
|
||||
|
||||
(defvar ekp--para-cache nil
|
||||
@ -250,6 +252,40 @@ Space boxes (preserved whitespace) need no additional glue."
|
||||
((eq 'mws type) ekp-mws-max-pixel)
|
||||
((eq 'cws type) ekp-cws-max-pixel)))
|
||||
|
||||
(defun ekp--para-glue-ideal (para type)
|
||||
"Get ideal glue pixel for TYPE using PARA's stored glue params."
|
||||
(let ((params (ekp-para-glue-params para)))
|
||||
(cond ((or (null type) (eq 'nws type)) 0)
|
||||
((eq 'lws type) (plist-get params :lws-ideal))
|
||||
((eq 'mws type) (plist-get params :mws-ideal))
|
||||
((eq 'cws type) (plist-get params :cws-ideal)))))
|
||||
|
||||
(defun ekp--para-glue-shrink (para type)
|
||||
"Get shrink amount for TYPE using PARA's stored glue params."
|
||||
(let ((params (ekp-para-glue-params para)))
|
||||
(cond ((or (null type) (eq 'nws type)) 0)
|
||||
((eq 'lws type) (plist-get params :lws-shrink))
|
||||
((eq 'mws type) (plist-get params :mws-shrink))
|
||||
((eq 'cws type) (plist-get params :cws-shrink)))))
|
||||
|
||||
(defun ekp--para-glue-stretch (para type)
|
||||
"Get stretch amount for TYPE using PARA's stored glue params."
|
||||
(let ((params (ekp-para-glue-params para)))
|
||||
(cond ((or (null type) (eq 'nws type)) 0)
|
||||
((eq 'lws type) (plist-get params :lws-stretch))
|
||||
((eq 'mws type) (plist-get params :mws-stretch))
|
||||
((eq 'cws type) (plist-get params :cws-stretch)))))
|
||||
|
||||
(defun ekp--para-glue-min (para type)
|
||||
"Get minimum glue pixel (ideal - shrink) for TYPE using PARA's stored params."
|
||||
(- (ekp--para-glue-ideal para type)
|
||||
(ekp--para-glue-shrink para type)))
|
||||
|
||||
(defun ekp--para-glue-max (para type)
|
||||
"Get maximum glue pixel (ideal + stretch) for TYPE using PARA's stored params."
|
||||
(+ (ekp--para-glue-ideal para type)
|
||||
(ekp--para-glue-stretch para type)))
|
||||
|
||||
;;; ============================================================
|
||||
;;; Cache Implementation: Fast Hash + Flat Structure
|
||||
;;; ============================================================
|
||||
@ -307,7 +343,7 @@ Computes ALL data in one pass: text, params, and prefix arrays."
|
||||
(aset max-prefixs (1+ i)
|
||||
(+ (aref max-prefixs i) box-w
|
||||
(ekp-glue-max-pixel glue-type)))))
|
||||
;; Create struct with all data
|
||||
;; Create struct with all data, including glue params at creation time
|
||||
(ekp-para--create
|
||||
:string string
|
||||
:latin-font latin-font
|
||||
@ -321,6 +357,15 @@ Computes ALL data in one pass: text, params, and prefix arrays."
|
||||
:ideal-prefixs ideal-prefixs
|
||||
:min-prefixs min-prefixs
|
||||
:max-prefixs max-prefixs
|
||||
:glue-params (list :lws-ideal ekp-lws-ideal-pixel
|
||||
:lws-stretch ekp-lws-stretch-pixel
|
||||
:lws-shrink ekp-lws-shrink-pixel
|
||||
:mws-ideal ekp-mws-ideal-pixel
|
||||
:mws-stretch ekp-mws-stretch-pixel
|
||||
:mws-shrink ekp-mws-shrink-pixel
|
||||
:cws-ideal ekp-cws-ideal-pixel
|
||||
:cws-stretch ekp-cws-stretch-pixel
|
||||
:cws-shrink ekp-cws-shrink-pixel)
|
||||
:dp-cache (make-hash-table :test 'eql :size 20))))
|
||||
|
||||
(defun ekp--get-para (string)
|
||||
@ -475,19 +520,20 @@ Returns (backptrs demerits rests gaps hyphen-counts fitness-classes line-counts)
|
||||
(list backptrs demerits rests gaps
|
||||
hyphen-counts fitness-classes line-counts)))
|
||||
|
||||
(defun ekp--dp-line-metrics (i k glues-types ideal-prefixs min-prefixs max-prefixs)
|
||||
"Compute line metrics for boxes I to K.
|
||||
(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.
|
||||
Returns (ideal-pixel min-pixel max-pixel) excluding leading glue."
|
||||
(let ((leading-glue-type (aref glues-types i)))
|
||||
(list (- (aref ideal-prefixs k) (aref ideal-prefixs i)
|
||||
(ekp-glue-ideal-pixel leading-glue-type))
|
||||
(ekp--para-glue-ideal para leading-glue-type))
|
||||
(- (aref min-prefixs k) (aref min-prefixs i)
|
||||
(ekp-glue-min-pixel leading-glue-type))
|
||||
(ekp--para-glue-min para leading-glue-type))
|
||||
(- (aref max-prefixs k) (aref max-prefixs i)
|
||||
(ekp-glue-max-pixel leading-glue-type)))))
|
||||
(ekp--para-glue-max para leading-glue-type)))))
|
||||
|
||||
(defun ekp--dp-force-break (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."
|
||||
(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.
|
||||
Uses PARA's stored glue params for consistency."
|
||||
(let* ((backptrs (nth 0 arrays))
|
||||
(demerits (nth 1 arrays))
|
||||
(rests (nth 2 arrays))
|
||||
@ -498,7 +544,7 @@ Returns (ideal-pixel min-pixel max-pixel) excluding leading glue."
|
||||
(hyphenate-p (ekp--hyphenate-p hyphen-positions break-pos))
|
||||
(ideal-pixel (- (aref ideal-prefixs break-pos)
|
||||
(aref ideal-prefixs i)
|
||||
(ekp-glue-ideal-pixel (aref glues-types i))))
|
||||
(ekp--para-glue-ideal para (aref glues-types i))))
|
||||
(rest-pixel (- line-pixel ideal-pixel)))
|
||||
(when hyphenate-p (cl-incf ideal-pixel hyphen-pixel))
|
||||
;; Force break with high demerits
|
||||
@ -610,7 +656,8 @@ If `ekp-use-c-module' is non-nil and C module is available, uses it."
|
||||
(defun ekp--prepare-para-for-batch (para line-pixel)
|
||||
"Prepare PARA data as vector for batch API at LINE-PIXEL.
|
||||
Returns [ideal-prefix min-prefix max-prefix glue-ideals glue-shrinks
|
||||
glue-stretches hyphen-positions hyphen-width line-width]."
|
||||
glue-stretches hyphen-positions hyphen-width line-width].
|
||||
Uses PARA's stored glue-params to ensure consistency with prefix arrays."
|
||||
(let* ((ideal-prefixs (ekp-para-ideal-prefixs para))
|
||||
(min-prefixs (ekp-para-min-prefixs para))
|
||||
(max-prefixs (ekp-para-max-prefixs para))
|
||||
@ -621,20 +668,19 @@ Returns [ideal-prefix min-prefix max-prefix glue-ideals glue-shrinks
|
||||
(glue-ideals (make-vector n 0))
|
||||
(glue-shrinks (make-vector n 0))
|
||||
(glue-stretches (make-vector n 0)))
|
||||
;; Use para's stored glue params, not global variables
|
||||
(dotimes (i n)
|
||||
(let ((type (aref glues-types i)))
|
||||
(aset glue-ideals i (ekp-glue-ideal-pixel type))
|
||||
(aset glue-shrinks i (- (ekp-glue-ideal-pixel type)
|
||||
(ekp-glue-min-pixel type)))
|
||||
(aset glue-stretches i (- (ekp-glue-max-pixel type)
|
||||
(ekp-glue-ideal-pixel type)))))
|
||||
(aset glue-ideals i (ekp--para-glue-ideal para type))
|
||||
(aset glue-shrinks i (ekp--para-glue-shrink para type))
|
||||
(aset glue-stretches i (ekp--para-glue-stretch para type))))
|
||||
(vector ideal-prefixs min-prefixs max-prefixs
|
||||
glue-ideals glue-shrinks glue-stretches
|
||||
hyphen-positions hyphen-pixel line-pixel)))
|
||||
|
||||
(defun ekp--store-batch-result (para line-pixel breaks cost)
|
||||
"Store batch result (BREAKS, COST) into PARA's dp-cache for LINE-PIXEL.
|
||||
Computes rests and gaps from breaks."
|
||||
Computes rests and gaps from breaks using PARA's stored glue params."
|
||||
(let* ((glues-types (ekp-para-glues-types para))
|
||||
(ideal-prefixs (ekp-para-ideal-prefixs para))
|
||||
(hyphen-positions (ekp-para-hyphen-positions para))
|
||||
@ -646,7 +692,7 @@ Computes rests and gaps from breaks."
|
||||
(end-with-hyphenp (ekp--hyphenate-p hyphen-positions (1- end)))
|
||||
(ideal-pixel (- (aref ideal-prefixs end)
|
||||
(aref ideal-prefixs start)
|
||||
(ekp-glue-ideal-pixel leading-glue-type))))
|
||||
(ekp--para-glue-ideal para leading-glue-type))))
|
||||
(when end-with-hyphenp
|
||||
(cl-incf ideal-pixel hyphen-pixel))
|
||||
(push (- line-pixel ideal-pixel) lines-rests)
|
||||
@ -705,7 +751,8 @@ Only processes strings that aren't already cached."
|
||||
(defun ekp--dp-cache-via-c (para string line-pixel)
|
||||
"Compute breaks using C module with Elisp's pre-computed prefix arrays.
|
||||
C module receives ALL font-dependent data from Elisp's para struct:
|
||||
prefix sums, glue values, hyphen info. C only does pure DP."
|
||||
prefix sums, glue values, hyphen info. C only does pure DP.
|
||||
Uses PARA's stored glue-params for consistency with cached prefix arrays."
|
||||
(ignore string) ; Use para's data instead
|
||||
(let* ((ideal-prefixs (ekp-para-ideal-prefixs para))
|
||||
(min-prefixs (ekp-para-min-prefixs para))
|
||||
@ -714,18 +761,16 @@ prefix sums, glue values, hyphen info. C only does pure DP."
|
||||
(hyphen-positions (ekp-para-hyphen-positions para))
|
||||
(hyphen-pixel (ekp-para-hyphen-pixel para))
|
||||
(n (length (ekp-para-boxes para)))
|
||||
;; Build glue value arrays for C
|
||||
;; Build glue value arrays for C using para's stored params
|
||||
(glue-ideals (make-vector n 0))
|
||||
(glue-shrinks (make-vector n 0))
|
||||
(glue-stretches (make-vector n 0)))
|
||||
;; Extract glue values from type symbols
|
||||
;; Extract glue values from para's stored params, not global variables
|
||||
(dotimes (i n)
|
||||
(let ((type (aref glues-types i)))
|
||||
(aset glue-ideals i (ekp-glue-ideal-pixel type))
|
||||
(aset glue-shrinks i (- (ekp-glue-ideal-pixel type)
|
||||
(ekp-glue-min-pixel type)))
|
||||
(aset glue-stretches i (- (ekp-glue-max-pixel type)
|
||||
(ekp-glue-ideal-pixel type)))))
|
||||
(aset glue-ideals i (ekp--para-glue-ideal para type))
|
||||
(aset glue-shrinks i (ekp--para-glue-shrink para type))
|
||||
(aset glue-stretches i (ekp--para-glue-stretch para type))))
|
||||
;; Call C module with all Elisp-computed arrays
|
||||
(let* ((result (ekp-c-break-with-arrays
|
||||
ideal-prefixs
|
||||
@ -746,13 +791,13 @@ prefix sums, glue values, hyphen info. C only does pure DP."
|
||||
(let* ((breaks c-breaks)
|
||||
(start 0)
|
||||
lines-rests lines-gaps)
|
||||
;; Compute rests and gaps for each line
|
||||
;; Compute rests and gaps for each line using para's stored params
|
||||
(dolist (end breaks)
|
||||
(let* ((leading-glue-type (aref glues-types start))
|
||||
(end-with-hyphenp (ekp--hyphenate-p hyphen-positions (1- end)))
|
||||
(ideal-pixel (- (aref ideal-prefixs end)
|
||||
(aref ideal-prefixs start)
|
||||
(ekp-glue-ideal-pixel leading-glue-type))))
|
||||
(ekp--para-glue-ideal para leading-glue-type))))
|
||||
(when end-with-hyphenp
|
||||
(cl-incf ideal-pixel hyphen-pixel))
|
||||
(push (- line-pixel ideal-pixel) lines-rests)
|
||||
@ -803,7 +848,7 @@ prefix sums, glue values, hyphen info. C only does pure DP."
|
||||
(end-with-hyphenp
|
||||
(ekp--hyphenate-p hyphen-positions (1- k)))
|
||||
(metrics (ekp--dp-line-metrics
|
||||
i k glues-types
|
||||
para i k glues-types
|
||||
ideal-prefixs min-prefixs max-prefixs))
|
||||
(ideal-pixel (nth 0 metrics))
|
||||
(min-pixel (nth 1 metrics))
|
||||
@ -818,7 +863,7 @@ prefix sums, glue values, hyphen info. C only does pure DP."
|
||||
(and is-last (> ideal-pixel line-pixel)))
|
||||
(when (null (aref demerits (1- k)))
|
||||
(ekp--dp-force-break
|
||||
i k arrays glues-types hyphen-positions
|
||||
para i k arrays glues-types hyphen-positions
|
||||
ideal-prefixs hyphen-pixel line-pixel))
|
||||
(throw 'break nil))
|
||||
;; Valid break point: compute demerits
|
||||
@ -919,9 +964,9 @@ Returns ((latin-adj . latin-extra) (mix-adj . mix-extra) (cjk-adj . cjk-extra)).
|
||||
(cons mix-adj mix-extra)
|
||||
(cons cjk-adj cjk-extra))))
|
||||
|
||||
(defun ekp--compute-glue-pixels (glues-types gaps-distribution stretch-p)
|
||||
(defun ekp--compute-glue-pixels (para glues-types gaps-distribution stretch-p)
|
||||
"Compute actual glue pixels from GLUES-TYPES and GAPS-DISTRIBUTION.
|
||||
Returns list of pixel values for each glue."
|
||||
Returns list of pixel values for each glue. Uses PARA's stored glue params."
|
||||
(let ((latin-adj (car (nth 0 gaps-distribution)))
|
||||
(latin-extra (cdr (nth 0 gaps-distribution)))
|
||||
(mix-adj (car (nth 1 gaps-distribution)))
|
||||
@ -931,7 +976,7 @@ Returns list of pixel values for each glue."
|
||||
(latin-idx -1) (mix-idx -1) (cjk-idx -1))
|
||||
(mapcar
|
||||
(lambda (type)
|
||||
(let* ((base (ekp-glue-ideal-pixel type))
|
||||
(let* ((base (ekp--para-glue-ideal para type))
|
||||
(adj (pcase type
|
||||
('lws (cl-incf latin-idx)
|
||||
(+ latin-adj (if (< latin-idx latin-extra) 1 0)))
|
||||
@ -949,27 +994,29 @@ Returns list of pixel values for each glue."
|
||||
(let ((trailing (- line-pixel box-width (if hyphen-p hyphen-pixel 0))))
|
||||
(list 0 trailing)))
|
||||
|
||||
(defun ekp--line-glue-last-line (glues-types ideal-pixel line-pixel)
|
||||
"Compute glues for last line (ragged right)."
|
||||
(defun ekp--line-glue-last-line (para glues-types ideal-pixel line-pixel)
|
||||
"Compute glues for last line (ragged right). Uses PARA's stored glue params."
|
||||
(append '(0)
|
||||
(mapcar #'ekp-glue-ideal-pixel glues-types)
|
||||
(mapcar (lambda (type) (ekp--para-glue-ideal para type)) glues-types)
|
||||
(list (- line-pixel ideal-pixel))))
|
||||
|
||||
(defun ekp--line-glue-normal (glues-types rest-pixel gaps-list)
|
||||
"Compute glues for a normal (justified) line."
|
||||
(defun ekp--line-glue-normal (para glues-types rest-pixel gaps-list)
|
||||
"Compute glues for a normal (justified) line. Uses PARA's stored glue params."
|
||||
(if (= rest-pixel 0)
|
||||
(append '(0) (mapcar #'ekp-glue-ideal-pixel glues-types) '(0))
|
||||
(append '(0) (mapcar (lambda (type) (ekp--para-glue-ideal para type)) glues-types) '(0))
|
||||
(let* ((stretch-p (> rest-pixel 0))
|
||||
(distribution (ekp--distribute-gap-adjustment
|
||||
(abs rest-pixel) gaps-list stretch-p))
|
||||
(glue-pixels (ekp--compute-glue-pixels glues-types distribution stretch-p)))
|
||||
(glue-pixels (ekp--compute-glue-pixels para glues-types distribution stretch-p)))
|
||||
(append '(0) glue-pixels '(0)))))
|
||||
|
||||
(defun ekp-line-glues (string line-pixel)
|
||||
"Compute glue pixels for each line after breaking STRING at LINE-PIXEL.
|
||||
Returns vector of vectors, each inner vector is glue pixels for one line.
|
||||
Each line's glues: [0 glue1 glue2 ... trailing-space]."
|
||||
(let* ((boxes-widths (ekp--boxes-widths string))
|
||||
Each line's glues: [0 glue1 glue2 ... trailing-space].
|
||||
Uses the cached para's stored glue params for consistency."
|
||||
(let* ((para (ekp--get-para string))
|
||||
(boxes-widths (ekp--boxes-widths string))
|
||||
(boxes-num (length (ekp--boxes string)))
|
||||
(glues-types (ekp--glues-types string))
|
||||
(hyphen-positions (ekp--hyphen-positions string))
|
||||
@ -990,10 +1037,10 @@ Each line's glues: [0 glue1 glue2 ... trailing-space]."
|
||||
(hyphen-p (ekp--hyphenate-p hyphen-positions (1- end)))
|
||||
(ideal-pixel (- (aref ideal-prefixs end)
|
||||
(aref ideal-prefixs start)
|
||||
(ekp-glue-ideal-pixel (aref glues-types start))))
|
||||
(ekp--para-glue-ideal para (aref glues-types start))))
|
||||
(max-pixel (+ (- (aref max-prefixs end)
|
||||
(aref max-prefixs start)
|
||||
(ekp-glue-max-pixel (aref glues-types start)))
|
||||
(ekp--para-glue-max para (aref glues-types start)))
|
||||
(if hyphen-p hyphen-pixel 0)))
|
||||
glue-list)
|
||||
(setq glue-list
|
||||
@ -1006,15 +1053,17 @@ Each line's glues: [0 glue1 glue2 ... trailing-space]."
|
||||
;; Last line: ragged right
|
||||
(is-last
|
||||
(ekp--line-glue-last-line
|
||||
line-glues-types ideal-pixel line-pixel))
|
||||
para line-glues-types ideal-pixel line-pixel))
|
||||
;; Forced break (line too short even at max stretch)
|
||||
((< max-pixel line-pixel)
|
||||
(append '(0)
|
||||
(mapcar #'ekp-glue-max-pixel line-glues-types)
|
||||
(mapcar (lambda (type)
|
||||
(ekp--para-glue-max para type))
|
||||
line-glues-types)
|
||||
(list (- line-pixel max-pixel))))
|
||||
;; Normal justified line
|
||||
(t
|
||||
(ekp--line-glue-normal line-glues-types
|
||||
(ekp--line-glue-normal para line-glues-types
|
||||
(nth i lines-rests)
|
||||
(nth i lines-gaps)))))
|
||||
(aset line-glues i (vconcat glue-list))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user