Markdown에서의 AA 입력을 대응해 보았다 【나의 암 가에타사이쿄의 게시판】
"모처럼 일본어 번역한 곳에서 AA 붙일 수 없다면 의미 없다!"
AA는 일본어 게시판의 필수적인 문화이다. 그러나 현대의 PC나 스마트폰에서는 AA가 올바르게 표시되지 않는 경우가 많다. 이것은 어떻게 든 해결하고 싶습니다.
하고 싶은 일
내 게시판은 Markdown 기법으로 쓰는 형태다. AA를 그대로 입력하면 굵은 글씨로 인식되거나 자동으로 개행이 들어가거나 한다. 2ch의 전브라 같은 느낌에 AA를 표시하고 싶다.
<aa>(´・ω・`)</aa>
자동으로 AA를 인식하는 것은 힘들 것 같기 때문에, AA 태그를 준비한다. AA 태그의 내용을 AA용 폰트로 바꾸어 Markdown 기법의 *bold*
등을 무시한다.
무리 야리 AA 태그 대응
이용하고 있는 Markdown 라이브러리는 russross/blackfriday 이다. 여러가지가 지금도 v1을 사용하고 있다.
이 라이브러리는 HTML 입력을 지원하지만 내용을 Markdown 기법으로 구문 분석합니다.
우선 포크하자. 그리고 markdown.go
에 blockTags
라는 지도를 살펴본다.
markdown.go// blockTags is a set of tags that are recognized as HTML block tags.
// Any of these can be included in markdown text without special escaping.
var blockTags = map[string]struct{}{
"blockquote": {},
"del": {},
"div": {},
// ...
"aa": {}, // ← 追加した
}
blockTags
에 들어 있는 태그는 블록(단락)이 되어, 이스케이프 되지 않으면 코멘트가 정중하게 가르쳐 주었다. 좋아, "aa": {},
를 추가한다. 이것으로 <aa>
의 내용은 Markdown가 되지 않고 그대로 출력된다.
XSS 대책으로서 microcosm-cc/bluemonday 를 사용하고 있다. blackfriday에서 Markdown을 HTML로 변환한 후 bluemonday에서 sanitize를 합니다. bluemonday에게 AA 태그를 허용해야합니다.
var sanitizer *bluemonday.Policy
func init() {
sanitizer = bluemonday.UGCPolicy() // よくある書式設定用のタグなどを許可
sanitizer.AllowNoAttrs().OnElements("aa") // 属性なしの<aa>を許可
}
이렇게 하지 않으면 <aa>
가 통째로 지워져 버린다.
그러나 비공식 HTML 태그를 그대로 출력하는 것은 기분 나쁘기 때문에 <aa>
를 <p class="aa">
로 바꾸자. PuerkitoBio/goquery 는 HTML을 만지기에 유용하다.
상기 처리를 조합하면 이렇게된다.
func renderMarkdown(content string) template.HTML {
// Markdownをrender
renderer := blackfriday.HtmlRendererWithParameters(commonHtmlFlags, "", "", blackfriday.HtmlRendererParameters{
// ...
})
md := blackfriday.MarkdownOptions([]byte(content), renderer, blackfriday.Options{
// ...
})
// XSS対策としてsanitize
sanitized := sanitizer.SanitizeBytes(md)
// goqueryを使ってHTMLをいじる
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(sanitized))
if err != nil {
panic(err)
}
// <aa>を<p class="aa">に変換
doc.Find("aa").Each(func(_ int, sel *goquery.Selection) {
sel.SetAttr("class", "aa")
sel.Nodes[0].Data = "p"
})
// <html><body>が勝手に追加されるので省略
html, err := doc.Find("body").Html()
if err != nil {
panic(err)
}
return template.HTML(html)
}
※ 사실은 panic없이 error를 반환해야합니다.
어쩌면 이런 처리는 markdown의 라이브러리 안에서 해야 할 것이지만, blackfriday의 코드는 위험으로 이쪽이 편하다. 실제 코드에서는 <aa>
의 대응뿐만 아니라 YouTube의 embed 등은 비슷한 수단으로 대응하고 있다.
AA를 좋은 느낌으로 표시
@scrpgil 씨는 멋진 AA 폰트 요약 를 제공해 주었으므로 참고로 했다. 엄청 살아난다. 감사합니다!
aahub_light 1 라는 글꼴은 가볍고 깨끗하기 때문에 사용하기로 했다. woff 파일을 S3에 올려서 캐시하도록 Cache-Control
헤더를 public, max-age=31536000, immutable
로 했다. AA용 CSS도 만들었다. 이것도 영원히 캐시 걸고 싶기 때문에 메인 CSS와 나누어 Cache-Control을 지정했다.
aa.css@font-face {
font-family: "aahub_light";
src: url("[CDNのURL]/aahub_light.woff") format("woff");
font-display: swap;
}
p.aa {
font-family: "aahub_light";
white-space: pre;
overflow: scroll;
word-break: keep-all;
overflow-wrap: normal;
/*font-size: 16px;*/
/*line-height: 18px;*/
}
스마트폰 등에서 큰 AA가 overflow하기 쉽기 때문에 overflow: scroll
로 했다. 가장 올바른 표시 방법은 font-size
와 line-height
를 지정할 필요가 있지만 스마트폰이라고 너무 커서 일단 지웠다. 그래도 어쩐지 괜찮았다.
결과
스마트폰에서도 AA는 좋은 느낌으로 표시된다. 했어!
「나의 칸가에사이쿄의 게시판」을 시리즈로서 다시 기사를 쓰고 싶기 때문에 다음도 잘 부탁드립니다!
htps : // 이 m/scrp1l/이고 ms/b8b로 1257아135d173585 ↩
Reference
이 문제에 관하여(Markdown에서의 AA 입력을 대응해 보았다 【나의 암 가에타사이쿄의 게시판】), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/guregu/items/4f4b1329114fdfdb5ded
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
<aa>(´・ω・`)</aa>
이용하고 있는 Markdown 라이브러리는 russross/blackfriday 이다. 여러가지가 지금도 v1을 사용하고 있다.
이 라이브러리는 HTML 입력을 지원하지만 내용을 Markdown 기법으로 구문 분석합니다.
우선 포크하자. 그리고
markdown.go
에 blockTags
라는 지도를 살펴본다.markdown.go
// blockTags is a set of tags that are recognized as HTML block tags.
// Any of these can be included in markdown text without special escaping.
var blockTags = map[string]struct{}{
"blockquote": {},
"del": {},
"div": {},
// ...
"aa": {}, // ← 追加した
}
blockTags
에 들어 있는 태그는 블록(단락)이 되어, 이스케이프 되지 않으면 코멘트가 정중하게 가르쳐 주었다. 좋아, "aa": {},
를 추가한다. 이것으로 <aa>
의 내용은 Markdown가 되지 않고 그대로 출력된다.XSS 대책으로서 microcosm-cc/bluemonday 를 사용하고 있다. blackfriday에서 Markdown을 HTML로 변환한 후 bluemonday에서 sanitize를 합니다. bluemonday에게 AA 태그를 허용해야합니다.
var sanitizer *bluemonday.Policy
func init() {
sanitizer = bluemonday.UGCPolicy() // よくある書式設定用のタグなどを許可
sanitizer.AllowNoAttrs().OnElements("aa") // 属性なしの<aa>を許可
}
이렇게 하지 않으면
<aa>
가 통째로 지워져 버린다.그러나 비공식 HTML 태그를 그대로 출력하는 것은 기분 나쁘기 때문에
<aa>
를 <p class="aa">
로 바꾸자. PuerkitoBio/goquery 는 HTML을 만지기에 유용하다.상기 처리를 조합하면 이렇게된다.
func renderMarkdown(content string) template.HTML {
// Markdownをrender
renderer := blackfriday.HtmlRendererWithParameters(commonHtmlFlags, "", "", blackfriday.HtmlRendererParameters{
// ...
})
md := blackfriday.MarkdownOptions([]byte(content), renderer, blackfriday.Options{
// ...
})
// XSS対策としてsanitize
sanitized := sanitizer.SanitizeBytes(md)
// goqueryを使ってHTMLをいじる
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(sanitized))
if err != nil {
panic(err)
}
// <aa>を<p class="aa">に変換
doc.Find("aa").Each(func(_ int, sel *goquery.Selection) {
sel.SetAttr("class", "aa")
sel.Nodes[0].Data = "p"
})
// <html><body>が勝手に追加されるので省略
html, err := doc.Find("body").Html()
if err != nil {
panic(err)
}
return template.HTML(html)
}
※ 사실은 panic없이 error를 반환해야합니다.
어쩌면 이런 처리는 markdown의 라이브러리 안에서 해야 할 것이지만, blackfriday의 코드는 위험으로 이쪽이 편하다. 실제 코드에서는
<aa>
의 대응뿐만 아니라 YouTube의 embed 등은 비슷한 수단으로 대응하고 있다.AA를 좋은 느낌으로 표시
@scrpgil 씨는 멋진 AA 폰트 요약 를 제공해 주었으므로 참고로 했다. 엄청 살아난다. 감사합니다!
aahub_light 1 라는 글꼴은 가볍고 깨끗하기 때문에 사용하기로 했다. woff 파일을 S3에 올려서 캐시하도록 Cache-Control
헤더를 public, max-age=31536000, immutable
로 했다. AA용 CSS도 만들었다. 이것도 영원히 캐시 걸고 싶기 때문에 메인 CSS와 나누어 Cache-Control을 지정했다.
aa.css@font-face {
font-family: "aahub_light";
src: url("[CDNのURL]/aahub_light.woff") format("woff");
font-display: swap;
}
p.aa {
font-family: "aahub_light";
white-space: pre;
overflow: scroll;
word-break: keep-all;
overflow-wrap: normal;
/*font-size: 16px;*/
/*line-height: 18px;*/
}
스마트폰 등에서 큰 AA가 overflow하기 쉽기 때문에 overflow: scroll
로 했다. 가장 올바른 표시 방법은 font-size
와 line-height
를 지정할 필요가 있지만 스마트폰이라고 너무 커서 일단 지웠다. 그래도 어쩐지 괜찮았다.
결과
스마트폰에서도 AA는 좋은 느낌으로 표시된다. 했어!
「나의 칸가에사이쿄의 게시판」을 시리즈로서 다시 기사를 쓰고 싶기 때문에 다음도 잘 부탁드립니다!
htps : // 이 m/scrp1l/이고 ms/b8b로 1257아135d173585 ↩
Reference
이 문제에 관하여(Markdown에서의 AA 입력을 대응해 보았다 【나의 암 가에타사이쿄의 게시판】), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/guregu/items/4f4b1329114fdfdb5ded
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
@font-face {
font-family: "aahub_light";
src: url("[CDNのURL]/aahub_light.woff") format("woff");
font-display: swap;
}
p.aa {
font-family: "aahub_light";
white-space: pre;
overflow: scroll;
word-break: keep-all;
overflow-wrap: normal;
/*font-size: 16px;*/
/*line-height: 18px;*/
}
스마트폰에서도 AA는 좋은 느낌으로 표시된다. 했어!
「나의 칸가에사이쿄의 게시판」을 시리즈로서 다시 기사를 쓰고 싶기 때문에 다음도 잘 부탁드립니다!
htps : // 이 m/scrp1l/이고 ms/b8b로 1257아135d173585 ↩
Reference
이 문제에 관하여(Markdown에서의 AA 입력을 대응해 보았다 【나의 암 가에타사이쿄의 게시판】), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/guregu/items/4f4b1329114fdfdb5ded텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)