Skip to content
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

Feature Request: HTML Markup Support in Encoder #41

Open
spaansba opened this issue Oct 25, 2024 · 0 comments
Open

Feature Request: HTML Markup Support in Encoder #41

spaansba opened this issue Oct 25, 2024 · 0 comments

Comments

@spaansba
Copy link

Is it possible to add an option to the ReceiptPrinterEncoder that allows users to input text with HTML markup?

Current Situation:
I've created a function that replaces HTML byte sequences with ESC/POS commands (implementation below), but it currently can't work with the ReceiptPrinterEncoder because HTML tags are being word-wrapped before they can be encoded.

Proposed Solution:
Could we have an option that ignores HTML tags during the word wrapping phase? This would allow the HTML tags to be converted to ESC/POS commands afterward.

My HTML to escpos bytes works like this:

  let HTMLByteToEscpos= new HTMLByteSequenceReplacer(encodedText)
  let x = HTMLByteToEscpos.bold().underline().encode()

Class code:

const CONTROL = {
  ESC: 0x1b,
  GS: 0x1d,
  FS: 0x1c,
  DC2: 0x12,
} as const

const TAGS = {
  LESS_THAN: 0x3c, //
  GREATER_THAN: 0x3e, // >
  SLASH: 0x2f, // /
  B: 0x62, // b
  U: 0x75, // u
} as const

const BOOL = {
  TRUE: 0x1,
  FALSE: 0x0,
} as const

const commands = {
  bold: {
    on: [CONTROL.ESC, 0x45, BOOL.TRUE],
    off: [CONTROL.ESC, 0x45, BOOL.FALSE],
    html: {
      open: [TAGS.LESS_THAN, TAGS.B, TAGS.GREATER_THAN], // <b>
      close: [TAGS.LESS_THAN, TAGS.SLASH, TAGS.B, TAGS.GREATER_THAN], // </b>
    },
  },
  underline: {
    on: [CONTROL.ESC, 0x2d, BOOL.TRUE],
    off: [CONTROL.ESC, 0x2d, BOOL.FALSE],
    html: {
      open: [TAGS.LESS_THAN, TAGS.U, TAGS.GREATER_THAN], // <u>
      close: [TAGS.LESS_THAN, TAGS.SLASH, TAGS.U, TAGS.GREATER_THAN], // </u>
    },
  },
} as const

type possibleTags = "underline" | "bold"

export class HTMLByteSequenceReplacer {
  private _bytes: Uint8Array

  constructor(bytes: Uint8Array) {
    this._bytes = bytes
  }

  underline(): HTMLByteSequenceReplacer {
    this._bytes = this.general("underline")
    return this
  }
  bold(): HTMLByteSequenceReplacer {
    this._bytes = this.general("bold")
    return this
  }

  private general(htmlTag: possibleTags) {
    const [replacedOpen, x] = findAndReplaceByteSequence(
      this._bytes,
      commands[htmlTag].html.open,
      commands[htmlTag].on
    )
    const [replacedClosed, y] = findAndReplaceByteSequence(
      replacedOpen,
      commands[htmlTag].html.close,
      commands[htmlTag].off
    )
    return replacedClosed
  }

  encode(): Uint8Array {
    return this._bytes
  }
}

export function findAndReplaceByteSequence(
  array: Uint8Array,
  findSequence: readonly number[],
  replaceSequence: readonly number[],
  startIndex: number = 0
): [Uint8Array, number] {
  let replacementCount = 0
  let matches: number[] = [] //Array of indexes of the start of each match

  // Find all matches in the users sequence
  for (let i = startIndex; i <= array.length - findSequence.length; i++) {
    let currentSequenceMatches = true
    for (let j = 0; j < findSequence.length; j++) {
      if (array[i + j] !== findSequence[j]) {
        currentSequenceMatches = false
        break
      }
    }
    if (currentSequenceMatches) {
      matches.push(i)
      replacementCount++
    }
  }

  // Calculate new array size since we need to create a new one and copy over the values
  const sizeDiff = replaceSequence.length - findSequence.length
  const newSize = array.length + sizeDiff * matches.length
  let newBitArray = new Uint8Array(newSize)


  let targetIndex = 0
  for (let i = 0; i < array.length; i++) {
    if (matches.includes(i)) {
      // Copy replacement sequence
      for (let j = 0; j < replaceSequence.length; j++) {
        newBitArray[targetIndex++] = replaceSequence[j]
      }
      // Skip the original sequence
      i += findSequence.length - 1
    } else {
      newBitArray[targetIndex++] = array[i]
    }
  }

  return [newBitArray, replacementCount]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant