-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix: ensure correct handling of mtable in LaTeX output #24
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1167,4 +1167,23 @@ describe('#convert', () => { | |
'V_{i} \\frac{\\Delta C_{A , i}^{t}}{\\Delta t} = \\sum_{j = k}^{N} G_{i , j}^{D} \\left(C_{A , j} - C_{A , i}\\right)', | ||
); | ||
}); | ||
describe('A mtable convertion example', () => { | ||
it('Mtable with many attributes', () => { | ||
const mathml = | ||
'<math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnwidth="3em 0.05em 3em 3em 3em 3em" columnspacing="0.0em" rowspacing="0.05ex" rowlines="solid" columnlines="solid" frame="solid" framespacing="0.0em 0ex"><mtr><mtd><mi>x</mi></mtd><mtd><mspace width="0.0em" height="2.5ex" depth="1.0ex"/></mtd><mtd><mo>−</mo><mn>1</mn></mtd><mtd><mn>0</mn></mtd><mtd><mn>1</mn></mtd><mtd><mn>2</mn></mtd></mtr><mtr><mtd><mi>g</mi><mrow><mo>(</mo><mi>x</mi><mo>)</mo></mrow></mtd><mtd><mspace width="0.0em" height="2.5ex" depth="1.0ex"/></mtd><mtd><mn>6</mn></mtd><mtd><mn>4</mn></mtd><mtd><mn>2</mn></mtd><mtd><mo>−</mo><mn>1</mn></mtd></mtr><mtr><mtd><msup><mi>g</mi><mo></mo></msup><mrow><mo>(</mo><mi>x</mi><mo>)</mo></mrow></mtd><mtd><mspace width="0.0em" height="2.5ex" depth="1.0ex"/></mtd><mtd><mo>−</mo><mn>1</mn></mtd><mtd><mo>−</mo><mn>7</mn></mtd><mtd><mo>−</mo><mn>2</mn></mtd><mtd><mo>−</mo><mn>3</mn></mtd></mtr></mtable></math>'; | ||
const result = MathMLToLaTeX.convert(mathml); | ||
//console.log('result', result) | ||
expect(result).toMatch( | ||
'\\begin{array}{|c|c|c|c|c|c|}\\hline\\hspace{1.269cm}{x }&\\hspace{0.021150000000000002cm}{ }&\\hspace{1.269cm}{ - 1 }&\\hspace{1.269cm}{ 0 }&\\hspace{1.269cm}{ 1 }&\\hspace{1.269cm}{ 2 } \\rule{0pt}{0.5pt}\\\\ \\hline \\\\\\hspace{1.269cm}{ g \\left(\\right. x \\left.\\right) }&\\hspace{0.021150000000000002cm}{ }&\\hspace{1.269cm}{ 6 }&\\hspace{1.269cm}{ 4 }&\\hspace{1.269cm}{ 2 }&\\hspace{1.269cm}{ - 1 } \\rule{0pt}{0.5pt}\\\\ \\hline \\\\\\hspace{1.269cm}{ g^{} \\left(\\right. x \\left.\\right) }&\\hspace{0.021150000000000002cm}{ }&\\hspace{1.269cm}{ - 1 }&\\hspace{1.269cm}{ - 7 }&\\hspace{1.269cm}{ - 2 }&\\hspace{1.269cm}{ - 3} \\rule{0pt}{0.5pt}\\\\ \\hline\\end{array}', | ||
); | ||
}); | ||
}); | ||
it('add begin array at the front', () => { | ||
const mathml = mathmlStrings.mtable; | ||
const result = MathMLToLaTeX.convert(mathml); | ||
console.log(result); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. forgot console.log |
||
expect(result).toMatch( | ||
'\\left(\\right. \\begin{array}{ccc}{1 }&{ 2 }&{ 3 } \\\\{ 4 }&{ 5 }&{ 6 } \\\\{ 7 }&{ 8 }&{ 9}\\end{array} \\left.\\right)', | ||
); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,16 @@ export class MTable implements ToLaTeXConverter { | |
|
||
constructor(mathElement: MathMLElement) { | ||
this._mathmlElement = mathElement; | ||
this._addFlagRecursiveIfName(this._mathmlElement.children, 'mtable', 'innerTable'); | ||
} | ||
// new method to turn string to numbers | ||
extractAndConvertNumbers(dimensionstring: string | undefined) { | ||
if (!dimensionstring) { | ||
//console.warn('dimensionString is undefined or null, default to 0.0'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dead console.log |
||
dimensionstring = '0.0, 0.0, 0.0, 0.0'; | ||
} | ||
const numberPattern = /-?\d+(\.\d+)?([eE][+-]?\d+)?/g; | ||
const matches = dimensionstring.match(numberPattern); | ||
return matches ? matches.map((num) => parseFloat(num)) : []; | ||
} | ||
|
||
convert(): string { | ||
|
@@ -16,21 +25,159 @@ export class MTable implements ToLaTeXConverter { | |
.map((converter) => converter.convert()) | ||
.join(' \\\\\n'); | ||
|
||
return this._hasFlag('innerTable') ? this._wrap(tableContent) : tableContent; | ||
const Maxcol: number = this._mathmlElement.children | ||
.map((e: MathMLElement): number => e.children.length) | ||
.reduce((a: number, b: number) => (a >= b ? a : b)); | ||
return this._wrapNestedTableContent(tableContent, Maxcol); | ||
} | ||
|
||
private _wrap(latex: string): string { | ||
return `\\begin{matrix}${latex}\\end{matrix}`; | ||
} | ||
private _wrapNestedTableContent(latex: string, Maxcol: number): string { | ||
//Calculate how many ccc do we need | ||
const col = Array.from({ length: Maxcol }) | ||
.map((e): string => 'c') | ||
.join(''); | ||
|
||
//columnwidth | ||
const columnwidth = this._mathmlElement.attributes['columnwidth']; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unnecessary comment and case: |
||
const widthValues = this.extractAndConvertNumbers(columnwidth); | ||
const columnwidthCm = widthValues.map((value) => value * 0.423); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. case There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
let TableRows = latex.split('\\\\'); | ||
TableRows = TableRows.map((row) => { | ||
if (row.trim() !== '') { | ||
let columns = row.split('&'); | ||
columns = columns.map((column, index) => { | ||
if (index < columnwidthCm.length) { | ||
return `\\hspace{${columnwidthCm[index]}cm}{${column}}`; | ||
} | ||
|
||
private _addFlagRecursiveIfName(mathmlElements: MathMLElement[], name: string, flag: string): void { | ||
mathmlElements.forEach((mathmlElement) => { | ||
if (mathmlElement.name === name) mathmlElement.attributes[flag] = flag; | ||
this._addFlagRecursiveIfName(mathmlElement.children, name, flag); | ||
return column; | ||
}); | ||
return columns.join('&'); | ||
} | ||
return row; | ||
}); | ||
} | ||
latex = TableRows.join(' \\\\'); | ||
|
||
//columnspacing | ||
const columnspacing = this._mathmlElement.attributes['columnspacing']; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. case |
||
// 1 em = 0.423 cm | ||
const columnspacingValue = parseFloat(columnspacing); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. case |
||
const columnspacingCm = isNaN(columnspacingValue) ? 0 : columnspacingValue * 0.423; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. case |
||
let tableRows = latex.split('\\\\'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is the semantic diff between tableRows and TableRows? |
||
|
||
// Process each row to add the horizontal space using \hspace | ||
tableRows = tableRows.map((row) => { | ||
if (row.trim() !== '') { | ||
let columns = row.split('&'); | ||
|
||
// Add the \hspace between each column | ||
columns = columns.map((column, index) => { | ||
if (index < columns.length - 1) | ||
if (!/\\hspace{.*?}/.test(column)) { | ||
return `${column} \\hspace{${columnspacingCm}cm}`; | ||
} else { | ||
return column; | ||
} | ||
return column; | ||
}); | ||
|
||
return columns.join('&'); | ||
} | ||
return row; | ||
}); | ||
|
||
latex = tableRows.join(' \\\\'); | ||
|
||
// rowspacing | ||
const rowspacing = this._mathmlElement.attributes['rowspacing']; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. uncessary comment |
||
let rows = latex.split('\\\\'); | ||
rows = rows.map((row) => { | ||
if (row.trim() !== '') { | ||
// Convert rowspacing from em to pt assuming 1em = 10pt (need furthur adjustment based on actual font size) | ||
const rowspacingPt: number = parseFloat(rowspacing || '0.0') * 10; | ||
// Add the \rule to the end of the row to create vertical space | ||
if (rowspacingPt === 0.0) { | ||
return `${row}`; | ||
} else { | ||
return `${row} \\rule{0pt}{${rowspacingPt}pt}`; | ||
} | ||
} | ||
return row; | ||
}); | ||
latex = rows.join(' \\\\'); | ||
// | ||
|
||
//rowlines | ||
const rowlines = this._mathmlElement.attributes['rowlines']; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. uncessary comment |
||
if (rowlines === 'solid') { | ||
let rows = latex.split('\\\\'); | ||
|
||
// Add \hline to the end of each row and join them back together | ||
rows = rows.map((row) => row.trim() + '\\\\ \\hline'); | ||
latex = rows.join(' \\\\'); | ||
} | ||
|
||
// | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. uncessary comment |
||
|
||
//columnlines | ||
const columnlines = this._mathmlElement.attributes['columnlines']; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. uncessary comment |
||
let columnSpec = col; | ||
if (columnlines === 'solid') { | ||
columnSpec = columnSpec.split('').join('|'); | ||
} | ||
// | ||
|
||
// frame | ||
let hLine = ''; | ||
const frame = this._mathmlElement.attributes['frame']; | ||
if (frame === 'solid') { | ||
columnSpec = '|' + columnSpec + '|'; | ||
hLine = '\\hline'; | ||
} | ||
// | ||
|
||
//framespacing | ||
const framespacing = this._mathmlElement.attributes['framespacing']; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. uncessary comment |
||
const spacingValues = this.extractAndConvertNumbers(framespacing); | ||
const horizontalSpacingEm = spacingValues[0] || 0; | ||
const verticalSpacingEx = spacingValues[1] || 0; | ||
let Tablerows = latex.split('\\\\'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. case |
||
// // Add vertical spacing to the first row | ||
if (Tablerows.length > 0) { | ||
Tablerows[0] = `\\rule{0pt}{${verticalSpacingEx}ex} ` + Tablerows[0]; | ||
} | ||
// // Add vertical spacing to the last row | ||
if (Tablerows.length > 1) { | ||
Tablerows[Tablerows.length - 1] += ` \\rule{0pt}{${verticalSpacingEx}ex}`; | ||
} | ||
Tablerows = Tablerows.map((row, rowIndex) => { | ||
if (row.trim() !== '') { | ||
let columns = row.split('&'); | ||
|
||
// Add horizontal spacing to the first column if \hspace does not already exist | ||
if (!/\\hspace{.*?}/.test(columns[0])) { | ||
if (horizontalSpacingEm === 0) { | ||
return columns[0]; | ||
} | ||
columns[0] = `\\hspace{${horizontalSpacingEm}em} ${columns[0]}`; | ||
} | ||
|
||
// Add horizontal spacing to the last column if \hspace does not already exist | ||
if (!/\\hspace{.*?}/.test(columns[columns.length - 1])) { | ||
if (horizontalSpacingEm === 0) { | ||
return columns[columns.length - 1]; | ||
} | ||
columns[columns.length - 1] = `${columns[columns.length - 1]} \\hspace{${horizontalSpacingEm}em}`; | ||
} | ||
|
||
return columns.join('&'); | ||
} | ||
return row; | ||
}); | ||
|
||
latex = latex.replace(/\\hspace\{0cm}/g, ''); | ||
|
||
private _hasFlag(flag: string): boolean { | ||
return !!this._mathmlElement.attributes[flag]; | ||
const screen_output = `\\begin{array}{${columnSpec}}${hLine}${latex}\\end{array}`; | ||
return screen_output; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dead commented code