LINE 공개 채팅에서 TeX를 사용하고 싶다면.

24880 단어 LINENext.jsTeXlifftech

모티프


수학과 공개 채팅에서는 어쨌든 조금 복잡한 공식을 보내고 싶을 때가 있다.
x = {-b+-√(b^2 -4ac)} / 2a

∑[n=1~∞]1/n^2 = π^2/6
등등...
너무 복잡하고 긴 것은 종이에 쓰거나 TeX 등의 레이아웃 처리 소프트웨어를 사용하여 그림을 보내야 한다위에서 열거한 (1행~수 행은 쓸 수 있다)식은 하나하나 그런 일을 하는 것은 번거롭다(순수한 TeX를 보내는 사람도 있지만 모르는 사람도 있어서 미묘하다)라고 쓰여 상대방에게 전달하는 경우가 많다.
컴퓨터라면 라인과 소프트웨어 창을 가로로 배열하면 되기 때문에 문제가 되기 어렵기 때문에 다음은 주로 스마트폰 사용자를 대상으로 대화를 나눈다.

구상


LINE에 Tex 같은 거 쓰면 편하죠~ 예전에는 가끔 이런 얘기가 나왔는데 실제로는 공개 채팅 이외에
https://qiita.com/plageoj/items/3df087999d550338e2b8
https://qiita.com/yudukikun5120/items/ec2fc172d180c099f24d
Messageing API를 사용한 전례가 있습니다.
왜 오픈채팅에서 예가 없지? 오픈채팅에서는 메사징 API를 사용할 수 없으니까.
TeXを送る -> botが処理して自動で画像を送る
의 절차를 실현할 수 없기 때문이다.
LINE 이외의 우리가 할 수 있는 일이 아니기 때문에 대체 수단으로
먼저
LINE上でTeXを書いて画像をダウンロードする -> 手動で画像を送る
의 절차가 떠오른다.이것LIFF을 사용하면 실현될 것 같다.공식 대응은 없다고 적혀 있지만 이번에는 프로필 정보나 친구 정보가 전혀 필요 없어 문제가 없는 것 같다.실제로 작성된 코드는 다음과 같습니다(Next.js).
index.ts
import { ChangeEvent, useEffect, useState } from 'react'
import Head from 'next/head'
import styles from '../styles/main.modules.css'
import katex from 'katex'
import html2canvas from 'html2canvas'


const downloadKatexImage = (katexArea: HTMLElement) => {
    html2canvas(katexArea)
        .then(canvas => {
            const downloadLink.href = document.getElementByTagName("a")[0]
            downloadLink.href = canvas.toDataURL()
            downloadLink.click()
        })
}
const renderKatex = (
    katexArea: HTMLElement,
    inputText: string
  ) => {
    katex.render(String.raw`${inputText}`, katexArea, {
        throwOnError: false,
        displayMode: true
    })
}
const getProperKatexFontSize = (
    katexArea: HTMLElement,
    currentFontSize: number
  ): number => {
    const katexBox = document.getElementsByClassName('katex')[0]
    // katexによって生成される、レンダリングされるKaTeXをすっぽり囲うspan要素

    const wRate = katexArea.clientWidth / katexBox.clientWidth
    const hRate = katexArea.clientHeight / katexBox.clientWidth

    return Min(
        Min(
            currentFontSize * wRate,
            defaultKatexFontSize
        ),
        Min(
            currentFontSize * hRate,
            defaultKatexFontSize
        )
    )
}

const deaultKatexFontSize = 3
const Min = (x:number, y:number) => {
    if (x <= y) return x
    return y
}


const Home = () => {
    const LiffID = process.env.LIFF_ID || ''
    useEffect(() => {
        import('@line/liff').then(liffFile => {
            const liff = liffFile.default
            liff.init({
                liffId: LiffID,
                withLoginOnEnternalBrowser: true
            })
        })
    }, [])

    const [katexFontsize, setKatexFontSize] = useState(defaultKatexFontSize)
    const [katexArea, setKatexArea] = useState<HTMLElement>()
    useEffect(() => {
        setKatexarea(
            document.getById('katex-area')!
        )
    }, [])

    const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
        renderKatex(katexArea!, e.target.value)
        const newKatexFontSize = getProperKatexFontSize(katexArea!, katexFontSize)
        setKatexFontsize(newKatexFontSize)
        document.documentElement.style.setProperty(
            '--katex-font-size', `${newKatexFontSize}em`
        )
    }
    const handleClick = () => {
        downloadKatexImage(katexArea!)
    }

    return (
        <Head>
          <title>LIFF-TEX</title>
          <link rel='icon' href='/favicon.ico'/>
        </Head>
        <main className={styles.mainStyle}>
          <div 
            className={styles.katexAreaStyle}
            id='katex-area'
          />
          <textarea
            className={styles.inputStyle}
            onChange={(e) => handleChange(e)}
          />
          <button className={styles.buttonStyle} onClick={handleClick}>
            download
          </button>
          <a download='KaTeX.png'></a>
        </main>
    )
}

export default Home
_document.ts
import { Html, Head, Main, NextScript } from "next/document";

const Document = () => {
  return (
    <Html>
      <Head>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css" integrity="sha384-KiWOvVjnN8qwAZbuQyWDIbfCLFhLXNETzBQjA/92pIowpC0d2O3nppDGQVgwd2nB" crossOrigin="anonymous"/>
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  )
}

export default Document
CSS 등은 생략했지만 여기에 실제 행동하는 것을 넣을 테니 가능하면 놀아주세요.
https://liff-tex-kana-rus.vercel.app
그러면 결론적으로 이렇게 하면 목적을 달성할 수 없다.TeX를 렌더링하기 전에는 문제가 없지만 이미지를 다운로드할 수 없습니다.조사해 보니 LINE 브라우저/LIFF 브라우저의 규격으로 인해 이미지뿐만 아니라 일반 파일의 다운로드도 불가능해졌다.
LINFF 브라우저는 iOS에서는 WKWebView를, 안드로이드에서는 안드로이드 WebView를 사용합니다.따라서 LIFF 브라우저의 사양과 동작도 이러한 메커니즘을 따라야 한다.
https://developers.line.biz/ja/docs/liff/overview/#liff-browser
https://pisuke-code.com/android-webview-download-file/
https://zenn.dev/bricklife/articles/how-to-download-blob-on-wkwebview
즉 LINE에 상응하는 설치를 하지 않으면 다운로드가 불가능하다는 것은 웹 애플리케이션에서도 구제불능이다.

다시 구상한다


그러면 어떻게 할 것인가, 크게 다음과 같은 두 가지 방침으로 나뉜다.
  • 외부에서 다른 API를 준비하고 우회 강행
  • 例: 画像のDataURLを取得
     -> 外部APIにPOST, 外部ブラウザで開かせる
     -> APIはアクセスされた瞬間POSTされた画像をダウンロードさせる
     -> 手動で1画面戻ってもらう (と自動で元のオープンチャットに遷移)
    
  • 외부 브라우저에서 처음 열기
  • 例: そもそもWebアプリを外部ブラウザで開かせる
     -> 画像のDataURL取得 & ダウンロードさせる
     -> 手動で1画面戻ってもらう (と自動で元のオープンチャットに遷移)
    
    문제는 외부 브라우저에 열려 있는 부분인데 LINE에서 URL을 열어도 LINE 브라우저에 열려 다운로드할 수 없습니다.
    나는 이런 물건을 발견했다.
    LINE 응용 프로그램 액세스를 통해 다음 질의 매개 변수가 추가된 URL에 액세스하면 LINE 내의 브라우저 대신 외부 브라우저에서 URL을 열 수 있습니다...
    https://developers.line.biz/ja/docs/messaging-api/using-line-url-scheme/
    이것을 사용하면 외부 브라우저에서 강제로 열 수 있습니다
    이 LINE URL 체계는 LINE 응용 프로그램에서 액세스하는 모든 URL에 적용됩니다.그러나 LIFF 응용 프로그램만 예외적으로 지원되지 않으므로 LINFF URL에 쿼리 매개변수를 추가해도 작동하지 않습니다.
    그래서이 방안은 불가능하다.즉, 적용된 URLhttps://liff-tex-kana-rus.vercel.app에 OpenExternal Browser=1 매개변수를 더한 것이다.
    https://liff-tex-kana-rus.vercel.app?openExternalBrowser=1
    공개 채팅하는 라디오에도 틀어놓고 필요할 때 클릭해 앱으로 날아가면 된다.
    솔직히 처음 구상한 형식과 크게 다르게 사전 조사의 부족함을 통감한다.이렇게 되면 일반적인 로컬 응용 프로그램과 큰 차이가 없다
    ネイティブ:
       手動でLINEを抜けてアプリを開く
    -> 書いてダウンロード
    -> 手動でアプリを抜けてLINEに移動する
    
    Web:
       (アナウンスしておけば) LINE上に表示されているURLでアプリに飛ぶ
    -> 書いてダウンロード
    -> 1画面戻る (と自動で元のオープンチャットに遷移)
    
    라는 시간차가 생겼기 때문에 웹 애플리케이션을 만드는 의미가 있다고 생각합니다.
    끝까지 읽어주셔서 감사합니다.

    좋은 웹페이지 즐겨찾기