pdfの全てのページをGyazoにアップロードしてScrapboxに貼り付けるUserScript
画質重視ならここを300などにすると良い
script.js/* MIT License Copyright (c) 2020 ci7lus */
import { importExternalJs } from "/api/code/ci7lus/userscript-utils/import-external-js.js"
import { importStyle } from "/api/code/ci7lus/userscript-utils/import-style.js"
await importExternalJs(
"https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.10.377/pdf.min.js"
)
await importExternalJs(
"https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.10.377/pdf.worker.min.js"
)
await importStyle(
"https://scrapbox.io/api/code/ci7lus/pdfの全てのページをGyazoにアップロードしてScrapboxに貼り付けるUserScript/style.css"
)
import "/api/code/ci7lus/pdfの全てのページをGyazoにアップロードしてScrapboxに貼り付けるUserScript/throttle.js"
import { insertText } from "/api/code/customize/scrapbox-insert-text/script.js"
scrapbox.PageMenu.addMenu({
title: "upload-pdf",
image: "/assets/img/logo_cut.svg",
onClick: async () => {
const input = document.createElement("input")
input.type = "file"
input.accept = "application/pdf,.pdf"
input.addEventListener("change", async () => {
if (input.files.length === 0) return
const file = input.files[0]
const filename = file.name
console.log(file)
// 進捗表示エリア
const progressArea = document.createElement("div")
progressArea.style =
"position: fixed; top: 0; right: 0; margin: 1rem; padding: 1rem; background: #FFF; color: 000; z-index: 9999;"
progressArea.innerText = "reading..."
document.body.appendChild(progressArea)
// "Error: The `Array.prototype` contains unexpected enumerable properties: getIndexByTitleLc; thus breaking e.g. `for...in` iteration of `Array`s."
// Array.prototype に変なプロパティが付いてると落ちる謎仕様になっていたのでアップロード中は削除する
const getIndexByTitleLc = Array.prototype.getIndexByTitleLc
delete Array.prototype.getIndexByTitleLc
try {
// File/BlobをArrayBufferに変換
const pdfObj = await new Promise((res, rej) => {
const reader = new FileReader()
reader.onerror = rej
reader.onload = () => {
res(reader.result)
}
reader.readAsArrayBuffer(file)
})
// cors制限のためcmapsはstorage.googleapis.comに上げたものを用いる
const pdf = await pdfjsLib.getDocument({
data: pdfObj,
cMapUrl:
"https://storage.googleapis.com/chrono-lexica/ci7lus-assets/pdfjs/cmaps/",
cMapPacked: true,
}).promise
const metadata = await pdf.getMetadata()
console.log(metadata)
const isBigImage = window.pdfscrap_big || false
progressArea.innerText = "pdf file loaded. Uploading to gyazo..."
let progress = 0
const updateProgressText = () => {
progressArea.innerText = `Uploading... (${progress++}/${
pdf.numPages
})`
}
const project = await fetch(
`https://scrapbox.io/api/projects/${scrapbox.Project.name}`
)
const { gyazoTeamsName } = await project.json()
if (gyazoTeamsName) {
alert(
"Gyazo Teamsを使ったことがないのでアップロードがおかしくなるかもしれません(可能であればTwitter@ci7lusまで動作確認報告お願いします)"
)
}
const gyazoOAuthToken = await fetch(
`https://scrapbox.io/api/login/gyazo/oauth-upload/token?gyazoTeamsName=${
gyazoTeamsName || ""
}`,
{
headers: {
accept: "application/json, text/plain, */*",
},
method: "GET",
}
)
const { token } = await gyazoOAuthToken.json()
const uploadPage = async (page) => {
const pdfPage = await pdf.getPage(page)
const viewport = pdfPage.getViewport({
scale: window.devicePixelRatio || 1.5,
})
const canvas = document.createElement("canvas")
const ctx = canvas.getContext("2d")
このrenderContextによってアップできる解像度が変更できる
PRINT_UNITSって何?
解像度を72(おそらく古いモニターの標準的な解像度72dpi)で割った係数
script.js const PRINT_RESOLUTION = 150;
const PRINT_UNITS = PRINT_RESOLUTION / 72.0;
const renderContext = {
canvasContext: ctx,
viewport: viewport,
transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0],
}
transformを追加
PRINT:RESOLUTIONを追加
デフォルトは150dpi
解像度による見え方の違い
図:解像度指定による見え方の違い(左;デフォルト 右;300dpi)
図:Gyazoで拡大するととても大きくなる
ちなみに同人誌を出版するときにカラー原稿を印刷するときの入稿解像度は350dpi
なんで72で割ってるんだろ?内部的な基準が72基準なのかな?
script.js canvas.height = viewport.height * PRINT_UNITS
canvas.width = viewport.width * PRINT_UNITS
canvasのサイズも大きくする必要があるらしい
script.js await pdfPage.render(renderContext).promise
/** @type {string} */
let imageUrl
if (token) {
console.info("Gyazo OAuth 経由でアップロードします")
const imageBlob = await new Promise((res) =>
canvas.toBlob(res, "image/jpeg", 0.95)
)
const formData = new FormData()
formData.append("imagedata", imageBlob)
formData.append("access_token", token)
formData.append("referer_url", location.href)
formData.append("title", filename)
while (true) {
// 登録順序を保証する
if (page - 1 <= progress) {
break
}
await new Promise((res) => setTimeout(() => res(), 100))
}
const upload = await fetch("https://upload.gyazo.com/api/upload", {
headers: {
accept: "application/json, text/plain, */*",
},
referrerPolicy: "same-origin",
body: formData,
method: "POST",
mode: "cors",
credentials: "omit",
})
const { permalink_url } = await upload.json()
imageUrl = permalink_url
} else {
console.info("Gyazo Easy_Auth 経由でアップロードします")
const dataUrl = canvas.toDataURL("image/jpeg")
const formData = new FormData()
formData.append("image_url", dataUrl)
formData.append(
"client_id",
"5a56f659c139358389c8c4838555135907d7edfbb98b9465aa6c51200e11dec5"
)
formData.append("referer_url", location.href)
formData.append("title", filename)
const easyAuth = await fetch(
`https://upload.gyazo.com/api/upload/easy_auth`,
{
method: "POST",
mode: "cors",
credentials: "include",
body: formData,
}
)
const uploadResult = await easyAuth.json()
while (true) {
// 登録順序を保証する
if (page - 1 <= progress) {
break
}
await new Promise((res) => setTimeout(() => res(), 100))
}
const getImage = await fetch(uploadResult.get_image_url, {
mode: "cors",
credentials: "include",
})
imageUrl = getImage.url
}
updateProgressText()
console.log(`page${page} -> ${imageUrl}`)
return imageUrl
}
const pages = await throttle.all(
[...Array(pdf.numPages).keys()]
.map((i) => i + 1)
.map((page) => () => uploadPage(page))
)
progressArea.innerText = "done"
const urls = pages.map((url) =>
isBigImage ? `[[${url}]]` : `[${url}]`
)
urls.unshift(file.name)
insertText({
text: urls.join("\n") + "\n",
})
} catch (e) {
console.log("failed", file, e)
alert(`failed to load: ${filename}`)
} finally {
document.body.removeChild(progressArea)
Array.prototype.getIndexByTitleLc = getIndexByTitleLc
}
})
input.click()
},
})