@@ -3620,3 +3620,312 @@ describe('m:groupChr converter', () => {
36203620 } ) ;
36213621 } ) ;
36223622} ) ;
3623+
3624+ describe ( 'm:m converter' , ( ) => {
3625+ it ( 'converts 2x2 matrix to <mtable> with <mtr> and <mtd>' , ( ) => {
3626+ const omml = {
3627+ name : 'm:oMath' ,
3628+ elements : [
3629+ {
3630+ name : 'm:m' ,
3631+ elements : [
3632+ {
3633+ name : 'm:mr' ,
3634+ elements : [
3635+ {
3636+ name : 'm:e' ,
3637+ elements : [ { name : 'm:r' , elements : [ { name : 'm:t' , elements : [ { type : 'text' , text : 'a' } ] } ] } ] ,
3638+ } ,
3639+ {
3640+ name : 'm:e' ,
3641+ elements : [ { name : 'm:r' , elements : [ { name : 'm:t' , elements : [ { type : 'text' , text : 'b' } ] } ] } ] ,
3642+ } ,
3643+ ] ,
3644+ } ,
3645+ {
3646+ name : 'm:mr' ,
3647+ elements : [
3648+ {
3649+ name : 'm:e' ,
3650+ elements : [ { name : 'm:r' , elements : [ { name : 'm:t' , elements : [ { type : 'text' , text : 'c' } ] } ] } ] ,
3651+ } ,
3652+ {
3653+ name : 'm:e' ,
3654+ elements : [ { name : 'm:r' , elements : [ { name : 'm:t' , elements : [ { type : 'text' , text : 'd' } ] } ] } ] ,
3655+ } ,
3656+ ] ,
3657+ } ,
3658+ ] ,
3659+ } ,
3660+ ] ,
3661+ } ;
3662+ const result = convertOmmlToMathml ( omml , doc ) ;
3663+ expect ( result ) . not . toBeNull ( ) ;
3664+ const mtable = result ! . querySelector ( 'mtable' ) ;
3665+ expect ( mtable ) . not . toBeNull ( ) ;
3666+ const rows = mtable ! . querySelectorAll ( 'mtr' ) ;
3667+ expect ( rows . length ) . toBe ( 2 ) ;
3668+ const cells = mtable ! . querySelectorAll ( 'mtd' ) ;
3669+ expect ( cells . length ) . toBe ( 4 ) ;
3670+ expect ( cells [ 0 ] ! . textContent ) . toBe ( 'a' ) ;
3671+ expect ( cells [ 1 ] ! . textContent ) . toBe ( 'b' ) ;
3672+ expect ( cells [ 2 ] ! . textContent ) . toBe ( 'c' ) ;
3673+ expect ( cells [ 3 ] ! . textContent ) . toBe ( 'd' ) ;
3674+ } ) ;
3675+
3676+ it ( 'returns null for empty matrix' , ( ) => {
3677+ const omml = {
3678+ name : 'm:oMath' ,
3679+ elements : [ { name : 'm:m' , elements : [ ] } ] ,
3680+ } ;
3681+ const result = convertOmmlToMathml ( omml , doc ) ;
3682+ expect ( result ) . toBeNull ( ) ;
3683+ } ) ;
3684+
3685+ it ( 'converts 1x3 row vector' , ( ) => {
3686+ const omml = {
3687+ name : 'm:oMath' ,
3688+ elements : [
3689+ {
3690+ name : 'm:m' ,
3691+ elements : [
3692+ {
3693+ name : 'm:mr' ,
3694+ elements : [
3695+ {
3696+ name : 'm:e' ,
3697+ elements : [ { name : 'm:r' , elements : [ { name : 'm:t' , elements : [ { type : 'text' , text : '1' } ] } ] } ] ,
3698+ } ,
3699+ {
3700+ name : 'm:e' ,
3701+ elements : [ { name : 'm:r' , elements : [ { name : 'm:t' , elements : [ { type : 'text' , text : '2' } ] } ] } ] ,
3702+ } ,
3703+ {
3704+ name : 'm:e' ,
3705+ elements : [ { name : 'm:r' , elements : [ { name : 'm:t' , elements : [ { type : 'text' , text : '3' } ] } ] } ] ,
3706+ } ,
3707+ ] ,
3708+ } ,
3709+ ] ,
3710+ } ,
3711+ ] ,
3712+ } ;
3713+ const result = convertOmmlToMathml ( omml , doc ) ;
3714+ const mtable = result ! . querySelector ( 'mtable' ) ;
3715+ expect ( mtable ) . not . toBeNull ( ) ;
3716+ const rows = mtable ! . querySelectorAll ( 'mtr' ) ;
3717+ expect ( rows . length ) . toBe ( 1 ) ;
3718+ const cells = mtable ! . querySelectorAll ( 'mtd' ) ;
3719+ expect ( cells . length ) . toBe ( 3 ) ;
3720+ } ) ;
3721+
3722+ it ( 'wraps each cell content in <mrow> inside <mtd>' , ( ) => {
3723+ const omml = {
3724+ name : 'm:oMath' ,
3725+ elements : [
3726+ {
3727+ name : 'm:m' ,
3728+ elements : [
3729+ {
3730+ name : 'm:mr' ,
3731+ elements : [
3732+ {
3733+ name : 'm:e' ,
3734+ elements : [
3735+ { name : 'm:r' , elements : [ { name : 'm:t' , elements : [ { type : 'text' , text : 'x' } ] } ] } ,
3736+ { name : 'm:r' , elements : [ { name : 'm:t' , elements : [ { type : 'text' , text : '+' } ] } ] } ,
3737+ { name : 'm:r' , elements : [ { name : 'm:t' , elements : [ { type : 'text' , text : 'y' } ] } ] } ,
3738+ ] ,
3739+ } ,
3740+ ] ,
3741+ } ,
3742+ ] ,
3743+ } ,
3744+ ] ,
3745+ } ;
3746+ const result = convertOmmlToMathml ( omml , doc ) ;
3747+ const mtd = result ! . querySelector ( 'mtd' ) ;
3748+ expect ( mtd ) . not . toBeNull ( ) ;
3749+ // Cell content sits under an <mrow>, not as direct <mtd> siblings.
3750+ expect ( mtd ! . children . length ) . toBe ( 1 ) ;
3751+ expect ( mtd ! . firstElementChild ! . localName ) . toBe ( 'mrow' ) ;
3752+ expect ( mtd ! . textContent ) . toBe ( 'x+y' ) ;
3753+ } ) ;
3754+
3755+ it ( 'preserves nested math objects in cells (fraction, superscript)' , ( ) => {
3756+ const omml = {
3757+ name : 'm:oMath' ,
3758+ elements : [
3759+ {
3760+ name : 'm:m' ,
3761+ elements : [
3762+ {
3763+ name : 'm:mr' ,
3764+ elements : [
3765+ {
3766+ name : 'm:e' ,
3767+ elements : [
3768+ {
3769+ name : 'm:f' ,
3770+ elements : [
3771+ {
3772+ name : 'm:num' ,
3773+ elements : [
3774+ { name : 'm:r' , elements : [ { name : 'm:t' , elements : [ { type : 'text' , text : 'a' } ] } ] } ,
3775+ ] ,
3776+ } ,
3777+ {
3778+ name : 'm:den' ,
3779+ elements : [
3780+ { name : 'm:r' , elements : [ { name : 'm:t' , elements : [ { type : 'text' , text : 'b' } ] } ] } ,
3781+ ] ,
3782+ } ,
3783+ ] ,
3784+ } ,
3785+ ] ,
3786+ } ,
3787+ {
3788+ name : 'm:e' ,
3789+ elements : [
3790+ {
3791+ name : 'm:sSup' ,
3792+ elements : [
3793+ {
3794+ name : 'm:e' ,
3795+ elements : [
3796+ { name : 'm:r' , elements : [ { name : 'm:t' , elements : [ { type : 'text' , text : 'z' } ] } ] } ,
3797+ ] ,
3798+ } ,
3799+ {
3800+ name : 'm:sup' ,
3801+ elements : [
3802+ { name : 'm:r' , elements : [ { name : 'm:t' , elements : [ { type : 'text' , text : '2' } ] } ] } ,
3803+ ] ,
3804+ } ,
3805+ ] ,
3806+ } ,
3807+ ] ,
3808+ } ,
3809+ ] ,
3810+ } ,
3811+ ] ,
3812+ } ,
3813+ ] ,
3814+ } ;
3815+ const result = convertOmmlToMathml ( omml , doc ) ;
3816+ const mtable = result ! . querySelector ( 'mtable' ) ;
3817+ expect ( mtable ! . querySelector ( 'mtd mfrac' ) ) . not . toBeNull ( ) ;
3818+ expect ( mtable ! . querySelector ( 'mtd msup' ) ) . not . toBeNull ( ) ;
3819+ } ) ;
3820+
3821+ it ( 'renders a placeholder in empty <m:e> cells by default (§22.1.2.83 plcHide="0")' , ( ) => {
3822+ const omml = {
3823+ name : 'm:oMath' ,
3824+ elements : [
3825+ {
3826+ name : 'm:m' ,
3827+ elements : [
3828+ {
3829+ name : 'm:mr' ,
3830+ elements : [
3831+ {
3832+ name : 'm:e' ,
3833+ elements : [ { name : 'm:r' , elements : [ { name : 'm:t' , elements : [ { type : 'text' , text : 'a' } ] } ] } ] ,
3834+ } ,
3835+ { name : 'm:e' } ,
3836+ {
3837+ name : 'm:e' ,
3838+ elements : [ { name : 'm:r' , elements : [ { name : 'm:t' , elements : [ { type : 'text' , text : 'c' } ] } ] } ] ,
3839+ } ,
3840+ ] ,
3841+ } ,
3842+ ] ,
3843+ } ,
3844+ ] ,
3845+ } ;
3846+ const result = convertOmmlToMathml ( omml , doc ) ;
3847+ const cells = result ! . querySelectorAll ( 'mtd' ) ;
3848+ expect ( cells . length ) . toBe ( 3 ) ;
3849+ expect ( cells [ 0 ] ! . textContent ) . toBe ( 'a' ) ;
3850+ expect ( cells [ 1 ] ! . textContent ) . toBe ( '\u25A1' ) ;
3851+ expect ( cells [ 2 ] ! . textContent ) . toBe ( 'c' ) ;
3852+ } ) ;
3853+
3854+ it ( 'hides empty-cell placeholders when m:plcHide is set (§22.1.2.83)' , ( ) => {
3855+ const omml = {
3856+ name : 'm:oMath' ,
3857+ elements : [
3858+ {
3859+ name : 'm:m' ,
3860+ elements : [
3861+ { name : 'm:mPr' , elements : [ { name : 'm:plcHide' , attributes : { 'm:val' : '1' } } ] } ,
3862+ {
3863+ name : 'm:mr' ,
3864+ elements : [
3865+ {
3866+ name : 'm:e' ,
3867+ elements : [ { name : 'm:r' , elements : [ { name : 'm:t' , elements : [ { type : 'text' , text : 'a' } ] } ] } ] ,
3868+ } ,
3869+ { name : 'm:e' } ,
3870+ {
3871+ name : 'm:e' ,
3872+ elements : [ { name : 'm:r' , elements : [ { name : 'm:t' , elements : [ { type : 'text' , text : 'c' } ] } ] } ] ,
3873+ } ,
3874+ ] ,
3875+ } ,
3876+ ] ,
3877+ } ,
3878+ ] ,
3879+ } ;
3880+ const result = convertOmmlToMathml ( omml , doc ) ;
3881+ const cells = result ! . querySelectorAll ( 'mtd' ) ;
3882+ expect ( cells . length ) . toBe ( 3 ) ;
3883+ expect ( cells [ 1 ] ! . textContent ) . toBe ( '' ) ;
3884+ } ) ;
3885+
3886+ it ( 'ignores m:mPr properties element' , ( ) => {
3887+ const omml = {
3888+ name : 'm:oMath' ,
3889+ elements : [
3890+ {
3891+ name : 'm:m' ,
3892+ elements : [
3893+ {
3894+ name : 'm:mPr' ,
3895+ elements : [
3896+ {
3897+ name : 'm:mcs' ,
3898+ elements : [
3899+ {
3900+ name : 'm:mc' ,
3901+ elements : [ { name : 'm:mcPr' , elements : [ { name : 'm:count' , attributes : { 'm:val' : '2' } } ] } ] ,
3902+ } ,
3903+ ] ,
3904+ } ,
3905+ ] ,
3906+ } ,
3907+ {
3908+ name : 'm:mr' ,
3909+ elements : [
3910+ {
3911+ name : 'm:e' ,
3912+ elements : [ { name : 'm:r' , elements : [ { name : 'm:t' , elements : [ { type : 'text' , text : 'a' } ] } ] } ] ,
3913+ } ,
3914+ {
3915+ name : 'm:e' ,
3916+ elements : [ { name : 'm:r' , elements : [ { name : 'm:t' , elements : [ { type : 'text' , text : 'b' } ] } ] } ] ,
3917+ } ,
3918+ ] ,
3919+ } ,
3920+ ] ,
3921+ } ,
3922+ ] ,
3923+ } ;
3924+ const result = convertOmmlToMathml ( omml , doc ) ;
3925+ const mtable = result ! . querySelector ( 'mtable' ) ;
3926+ expect ( mtable ) . not . toBeNull ( ) ;
3927+ const cells = mtable ! . querySelectorAll ( 'mtd' ) ;
3928+ expect ( cells . length ) . toBe ( 2 ) ;
3929+ expect ( mtable ! . textContent ) . toBe ( 'ab' ) ;
3930+ } ) ;
3931+ } ) ;
0 commit comments