Below is the snippet I use for dynamics with expressive text. I believe I've posted it to the mailing list before, a few years ago maybe.
\version "2.18.2"
dynText = #(define-event-function (parser location dyn expr) (markup? markup?)
(let* (
(mark #{ \markup { \dynamic $dyn \normal-text\italic $expr } #})
(offset (lambda (grob)
(let* (
(layout (ly:grob-layout grob))
(props (ly:grob-alist-chain grob
(ly:output-def-lookup layout 'text-font-defaults)))
(dyn-X-extent
(ly:stencil-extent
(ly:text-interface::interpret-markup layout props dyn)
X))
(width (abs
(- (cdr dyn-X-extent) (car dyn-X-extent))))
)
(- 1 (/ width 2))
)
)
)
)
#{
\tweak DynamicText.X-offset #offset
#(make-dynamic-script mark)
#}
)
)
%% Example
% \paper {
% ragged-right = ##f
% indent = 0\cm
% }
%
% \new Staff \with {
% \omit TimeSignature
% } \relative c' {
% c1\dynText "p" "sub."
% c1\dynText "fff" "espressivo"
% c1\dynText "p" "espressivo"
% c1\dynText "fffff" "sub."
% \break
% c1\p
% c1\fff
% c1\p
% c1\fffff
% }
%