With the help of Rene a program which is displayed correctly here: util.JSON.parse helps to import non ascii symbols printable in this forum
IMPORT util
MAIN
DEFINE s, s2,smil1,smil2,pound STRING
IF fgl_getenv("FGL_LENGTH_SEMANTICS") == "CHAR" THEN
DISPLAY "must not have CHAR length semantics to count bytes"
RETURN
END IF
--smileys need 2 unicode code points (beyond Unicode basic plane)
CALL util.JSON.parse('"\\uD83D\\uDE01"',smil1)
DISPLAY "smil1:",smil1
CALL util.JSON.parse('"\\uD83D\\uDE02"',smil2)
DISPLAY "smil2:",smil2
CALL util.JSON.parse('"\\u00a3"',pound)
DISPLAY "pound:",pound
LET s = smil1,smil2,"ab",pound,"c",smil1
LET s2 = cutAtByteLength(s, 14) --last smil1 is cut because the byte boundary is inbetween
DISPLAY SFMT("s:%1 slen:%2 s2:%3 s2len:%4", s, length(s), s2, length(s2))
LET s = smil1,smil2,"x"
LET s2 = cutAtByteLength(s, 8)
DISPLAY SFMT("s:%1 slen:%2 s2:%3 s2len:%4", s, length(s), s2, length(s2))
END MAIN
FUNCTION cutAtByteLength(s STRING, maxByteLength INT) RETURNS STRING
VAR l = s.getLength() --byte length whole string
DISPLAY sfmt("source:'%1' has length:%2",s,l)
VAR i = 1
WHILE i <= l
VAR c = s.getCharAt(i)
VAR clen = c.getLength() --byte length one char
LET i += clen
DISPLAY SFMT("i:%1,c:%2,len:%3", i, c, c.getLength())
IF i - 1 > maxByteLength THEN
RETURN s.subString(1, i - 1 - clen)
END IF
END WHILE
RETURN s
END FUNCTION