CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-mathjax

JavaScript display engine for LaTeX, MathML, and AsciiMath mathematical notation in browsers and Node.js

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

output-formats.mddocs/

Output Formats

MathJax supports two high-quality output formats: CommonHTML (HTML/CSS) and SVG (Scalable Vector Graphics). Both formats provide excellent rendering quality, accessibility support, and extensive customization options.

Capabilities

CommonHTML Output

Render mathematics using HTML elements with CSS styling, providing excellent performance and integration with web content.

/**
 * Get CommonHTML stylesheet
 * @returns CSS stylesheet element for CommonHTML output
 */
function chtmlStylesheet(): Element;

/**
 * Get metrics for CommonHTML element
 * @param wrapper - Element wrapper to measure
 * @param display - Whether element is in display mode
 * @returns Metrics object with measurements
 */
function getMetricsFor(wrapper: Element, display: boolean): Metrics;

Usage Examples:

// Convert to CommonHTML
const htmlElement = MathJax.tex2chtml('E = mc^2');
document.body.appendChild(htmlElement);

// Get stylesheet for styling
const stylesheet = MathJax.chtmlStylesheet();
document.head.appendChild(stylesheet);

// Get metrics for positioning
const metrics = MathJax.getMetricsFor(htmlElement, false);
console.log('Width:', metrics.em, 'em');

SVG Output

Render mathematics as scalable vector graphics, providing perfect scaling and print quality.

/**
 * Get SVG stylesheet
 * @returns CSS stylesheet element for SVG output
 */
function svgStylesheet(): Element;

/**
 * Get metrics for SVG element
 * @param wrapper - Element wrapper to measure
 * @param display - Whether element is in display mode
 * @returns Metrics object with measurements
 */
function getMetricsFor(wrapper: Element, display: boolean): Metrics;

Usage Examples:

// Convert to SVG
const svgElement = MathJax.tex2svg('\\frac{x^2}{2}');
document.body.appendChild(svgElement);

// Get stylesheet for SVG styling
const stylesheet = MathJax.svgStylesheet();
document.head.appendChild(stylesheet);

// Get SVG metrics
const metrics = MathJax.getMetricsFor(svgElement, true);
console.log('SVG dimensions:', metrics);

Configuration Options

CommonHTML Configuration

interface CommonHTMLOptions {
  /** Scaling factor for output */
  scale?: number;
  /** Minimum scaling factor */
  minScale?: number;
  /** Match container font height */
  matchFontHeight?: boolean;
  /** Inherit font for mtext elements */
  mtextInheritFont?: boolean;
  /** Inherit font for merror elements */
  merrorInheritFont?: boolean;
  /** Use MathML spacing rules */
  mathmlSpacing?: boolean;
  /** Attributes to skip when processing */
  skipAttributes?: Record<string, boolean>;
  /** Ex-height factor */
  exFactor?: number;
  /** Display alignment */
  displayAlign?: 'left' | 'center' | 'right';
  /** Display indentation */
  displayIndent?: string;
  /** Font URL for web fonts */
  fontURL?: string;
  /** Use adaptive CSS */
  adaptiveCSS?: boolean;
}

Configuration Example:

MathJax.config.chtml = {
  scale: 1.2,
  minScale: 0.5,
  matchFontHeight: true,
  mtextInheritFont: true,
  merrorInheritFont: true,
  mathmlSpacing: true,
  exFactor: 0.5,
  displayAlign: 'center',
  displayIndent: '2em',
  fontURL: 'https://cdn.jsdelivr.net/npm/mathjax@4/fonts/woff-v2',
  adaptiveCSS: true,
  skipAttributes: {
    'data-semantic-type': true,
    'data-semantic-role': true
  }
};

SVG Configuration

interface SVGOptions {
  /** Scaling factor for output */
  scale?: number;
  /** Minimum scaling factor */
  minScale?: number;
  /** Inherit font for mtext elements */
  mtextInheritFont?: boolean;
  /** Inherit font for merror elements */
  merrorInheritFont?: boolean;
  /** Use MathML spacing rules */
  mathmlSpacing?: boolean;
  /** Attributes to skip when processing */
  skipAttributes?: Record<string, boolean>;
  /** Ex-height factor */
  exFactor?: number;
  /** Display alignment */
  displayAlign?: 'left' | 'center' | 'right';
  /** Display indentation */
  displayIndent?: string;
  /** Font caching mode */
  fontCache?: 'local' | 'global' | 'none';
  /** Local ID prefix */
  localID?: string;
  /** Include internal speech titles */
  internalSpeechTitles?: boolean;
}

Configuration Example:

MathJax.config.svg = {
  scale: 1.0,
  minScale: 0.5,
  mtextInheritFont: false,
  merrorInheritFont: false,
  mathmlSpacing: true,
  exFactor: 0.5,
  displayAlign: 'center',
  displayIndent: '0',
  fontCache: 'local',
  localID: 'MJX-SVG',
  internalSpeechTitles: true,
  skipAttributes: {
    'data-mml-node': true
  }
};

Font Management

CommonHTML Fonts

CommonHTML output uses web fonts for optimal rendering:

// Font configuration
MathJax.config.chtml = {
  fontURL: 'https://cdn.jsdelivr.net/npm/mathjax@4/fonts/woff-v2',
  adaptiveCSS: true,
  matchFontHeight: true
};

// Custom font paths
MathJax.config.chtml = {
  fontURL: '/static/mathjax-fonts',
  fontFormat: 'woff2'  // woff, woff2, otf, ttf
};

// Disable web fonts (use system fonts)
MathJax.config.chtml = {
  fontURL: null
};

SVG Fonts

SVG output embeds font information directly:

// Font caching options
MathJax.config.svg = {
  fontCache: 'local',   // Cache fonts per element
  // fontCache: 'global', // Global font cache
  // fontCache: 'none'    // No font caching
};

// Font loading
MathJax.config.options = {
  loadAllFontFiles: true  // Preload all font files
};

Output Customization

Styling and Appearance

// Custom CSS for CommonHTML
const style = document.createElement('style');
style.textContent = `
  .mjx-chtml {
    font-size: 120%;
    color: #333;
  }
  
  .mjx-chtml .mjx-math {
    border: 1px solid #ddd;
    padding: 5px;
    border-radius: 3px;
  }
  
  .mjx-display {
    text-align: center;
    margin: 1em 0;
  }
`;
document.head.appendChild(style);

// Custom CSS for SVG
const svgStyle = document.createElement('style');
svgStyle.textContent = `
  .mjx-svg {
    background: #f9f9f9;
    border-radius: 4px;
  }
  
  .mjx-svg svg {
    filter: drop-shadow(1px 1px 2px rgba(0,0,0,0.1));
  }
`;
document.head.appendChild(svgStyle);

Display Options

// Configure display alignment
MathJax.config.chtml = {
  displayAlign: 'left',
  displayIndent: '2em'
};

// Configure scaling
MathJax.config.svg = {
  scale: 1.5,
  minScale: 0.8
};

// Per-conversion options
const options = {
  display: true,
  scale: 1.2,
  em: 16,
  ex: 8,
  containerWidth: 1200
};

const result = MathJax.tex2svg('x^2 + y^2 = z^2', options);

Performance Optimization

Output Format Selection

// Choose format based on use case
function selectOptimalFormat(context) {
  if (context.needsScaling) {
    return 'svg';  // SVG scales perfectly
  }
  
  if (context.needsInteraction) {
    return 'chtml';  // HTML integrates better with DOM
  }
  
  if (context.needsPrint) {
    return 'svg';  // SVG prints at high quality
  }
  
  return 'chtml';  // Default to CommonHTML for web
}

// Dynamic format switching
async function renderWithOptimalFormat(math, context) {
  const format = selectOptimalFormat(context);
  
  if (format === 'svg') {
    return MathJax.tex2svg(math);
  } else {
    return MathJax.tex2chtml(math);
  }
}

Caching and Reuse

// Cache rendered mathematics
class MathCache {
  constructor() {
    this.cache = new Map();
  }
  
  render(expression, format, options = {}) {
    const key = JSON.stringify({ expression, format, options });
    
    if (this.cache.has(key)) {
      return this.cache.get(key).cloneNode(true);
    }
    
    let result;
    if (format === 'svg') {
      result = MathJax.tex2svg(expression, options);
    } else {
      result = MathJax.tex2chtml(expression, options);
    }
    
    this.cache.set(key, result.cloneNode(true));
    return result;
  }
  
  clear() {
    this.cache.clear();
  }
}

const mathCache = new MathCache();
const cached = mathCache.render('E = mc^2', 'svg');

Advanced Features

Custom Output Processing

// Post-process CommonHTML output
function customizeCommonHTML(element) {
  // Add custom attributes
  element.setAttribute('data-math-type', 'equation');
  
  // Add accessibility improvements
  const mathElements = element.querySelectorAll('.mjx-math');
  mathElements.forEach(math => {
    math.setAttribute('role', 'img');
    math.setAttribute('aria-label', 'Mathematical expression');
  });
  
  // Add custom styling
  element.classList.add('custom-math');
  
  return element;
}

// Apply custom processing
const rawHTML = MathJax.tex2chtml('x^2 + y^2 = z^2');
const customHTML = customizeCommonHTML(rawHTML);

SVG Manipulation

// Post-process SVG output
function customizeSVG(svgElement) {
  const svg = svgElement.querySelector('svg');
  
  // Add custom attributes
  svg.setAttribute('data-math-rendered', 'true');
  
  // Modify viewBox for custom sizing
  const viewBox = svg.getAttribute('viewBox');
  if (viewBox) {
    const [x, y, width, height] = viewBox.split(' ').map(Number);
    svg.setAttribute('viewBox', `${x-5} ${y-5} ${width+10} ${height+10}`);
  }
  
  // Add background rectangle
  const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
  rect.setAttribute('x', '0');
  rect.setAttribute('y', '0');
  rect.setAttribute('width', '100%');
  rect.setAttribute('height', '100%');
  rect.setAttribute('fill', '#f8f8f8');
  rect.setAttribute('stroke', '#ddd');
  svg.insertBefore(rect, svg.firstChild);
  
  return svgElement;
}

// Apply SVG customization
const rawSVG = MathJax.tex2svg('\\int_0^\\infty e^{-x} dx');
const customSVG = customizeSVG(rawSVG);

Responsive Mathematics

// Make mathematics responsive
function makeResponsive(mathElement, maxWidth = 600) {
  const svg = mathElement.querySelector('svg');
  if (svg) {
    // Make SVG responsive
    svg.style.maxWidth = '100%';
    svg.style.height = 'auto';
    
    // Scale down for small screens
    const actualWidth = parseFloat(svg.getAttribute('width'));
    if (actualWidth > maxWidth) {
      const scale = maxWidth / actualWidth;
      svg.style.transform = `scale(${scale})`;
      svg.style.transformOrigin = 'left center';
    }
  }
  
  return mathElement;
}

// Apply responsive design
const math = MathJax.tex2svg('\\sum_{i=1}^{n} \\frac{1}{i^2} = \\frac{\\pi^2}{6}');
const responsive = makeResponsive(math);

Integration Examples

Print Styling

// Print-optimized CSS
const printStyle = document.createElement('style');
printStyle.textContent = `
  @media print {
    .mjx-chtml {
      font-size: 12pt;
      color: black !important;
    }
    
    .mjx-svg svg {
      max-width: 100% !important;
      height: auto !important;
    }
    
    .mjx-display {
      page-break-inside: avoid;
      margin: 0.5em 0;
    }
  }
`;
document.head.appendChild(printStyle);

Dark Mode Support

// Dark mode mathematics
function applyDarkMode(isDark) {
  const style = document.createElement('style');
  style.id = 'mathjax-dark-mode';
  
  if (isDark) {
    style.textContent = `
      .mjx-chtml {
        color: #e0e0e0 !important;
      }
      
      .mjx-svg {
        filter: invert(1) hue-rotate(180deg);
      }
    `;
  } else {
    style.textContent = '';
  }
  
  // Replace existing dark mode styles
  const existing = document.getElementById('mathjax-dark-mode');
  if (existing) {
    existing.remove();
  }
  
  document.head.appendChild(style);
}

// Apply dark mode
applyDarkMode(true);

Metrics and Measurements

Output Metrics

interface Metrics {
  /** Em size in pixels */
  em: number;
  /** Ex height in pixels */
  ex: number;
  /** Container width */
  containerWidth: number;
  /** Scale factor */
  scale: number;
  /** Line width setting */
  lineWidth?: number;
  /** Actual scale factor used */
  scaleFactor?: number;  
  /** Font scaling factor */
  fontScale?: number;
}

Usage Examples:

// Get element metrics
const element = MathJax.tex2chtml('x^2 + y^2 = z^2');
const metrics = MathJax.getMetricsFor(element, false);

console.log('Em size:', metrics.em);
console.log('Ex height:', metrics.ex);
console.log('Scale:', metrics.scale);

// Use metrics for positioning
element.style.marginTop = `${metrics.ex * 0.5}px`;
element.style.marginBottom = `${metrics.ex * 0.5}px`;

// Responsive scaling based on metrics
const scaleFactor = Math.min(1.0, 800 / metrics.containerWidth);
element.style.transform = `scale(${scaleFactor})`;

Bounding Box Calculations

// Get bounding box information
function getMathBoundingBox(mathElement) {
  const svg = mathElement.querySelector('svg');
  if (svg) {
    const viewBox = svg.getAttribute('viewBox');
    if (viewBox) {
      const [x, y, width, height] = viewBox.split(' ').map(Number);
      return { x, y, width, height };
    }
  }
  
  // For CommonHTML, use DOM measurements
  const rect = mathElement.getBoundingClientRect();
  return {
    x: 0,
    y: 0,
    width: rect.width,
    height: rect.height
  };
}

// Position mathematics using bounding box
const math = MathJax.tex2svg('\\frac{a}{b}');
const bbox = getMathBoundingBox(math);
math.style.position = 'absolute';
math.style.left = `${100 - bbox.width / 2}px`;
math.style.top = `${50 - bbox.height / 2}px`;

docs

accessibility.md

document-rendering.md

extensions.md

index.md

initialization.md

input-processing.md

output-formats.md

tile.json