v0.5.0 - convert svg directly to pdf to preserve quality - #4
This commit is contained in:
parent
ac127c7b25
commit
4612c2bbc2
5 changed files with 2642 additions and 99 deletions
2634
dist/main.js
vendored
2634
dist/main.js
vendored
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "musescore-downloader",
|
||||
"version": "0.4.0",
|
||||
"version": "0.5.0",
|
||||
"description": "download sheet music from musescore.com for free, no login or Musescore Pro required | 免登录、免 Musescore Pro,免费下载 musescore.com 上的曲谱",
|
||||
"main": "dist/main.js",
|
||||
"repository": {
|
||||
|
@ -14,7 +14,8 @@
|
|||
},
|
||||
"homepage": "https://github.com/Xmader/musescore-downloader#readme",
|
||||
"dependencies": {
|
||||
"pdfkit": "git+https://github.com/Xmader/pdfkit.git"
|
||||
"pdfkit": "git+https://github.com/Xmader/pdfkit.git",
|
||||
"svg-to-pdfkit": "^0.1.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-json": "^4.0.0",
|
||||
|
|
46
src/main.ts
46
src/main.ts
|
@ -10,41 +10,7 @@ const saveAs: typeof import("file-saver").saveAs = FileSaver.saveAs
|
|||
|
||||
let pdfBlob: Blob
|
||||
|
||||
const imgToBlob = async (imgURL: string) => {
|
||||
const imageElement = document.createElement("img")
|
||||
imageElement.style.display = "none"
|
||||
document.body.appendChild(imageElement)
|
||||
|
||||
imageElement.src = imgURL
|
||||
|
||||
// wait until image loaded
|
||||
await new Promise((resolve) => {
|
||||
imageElement.onload = () => resolve()
|
||||
})
|
||||
|
||||
const { naturalWidth: width, naturalHeight: height } = imageElement
|
||||
|
||||
const canvas = document.createElement("canvas")
|
||||
const canvasContext = canvas.getContext("2d")
|
||||
|
||||
canvas.width = width
|
||||
canvas.height = height
|
||||
canvas.style.display = "none"
|
||||
|
||||
document.body.appendChild(canvas)
|
||||
|
||||
canvasContext.clearRect(0, 0, width, height)
|
||||
canvasContext.drawImage(imageElement, 0, 0)
|
||||
|
||||
const data: Blob = await new Promise(resolve => canvas.toBlob(resolve, "image/png"))
|
||||
|
||||
canvas.remove()
|
||||
imageElement.remove()
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
const generatePDF = async (svgURLs: string[], name?: string) => {
|
||||
const generatePDF = async (imgURLs: string[], imgType: "svg" | "png", name?: string) => {
|
||||
if (pdfBlob) {
|
||||
return saveAs(pdfBlob, `${name}.pdf`)
|
||||
}
|
||||
|
@ -52,10 +18,8 @@ const generatePDF = async (svgURLs: string[], name?: string) => {
|
|||
const cachedImg = document.querySelector("img[id^=score_]") as HTMLImageElement
|
||||
const { naturalWidth: width, naturalHeight: height } = cachedImg
|
||||
|
||||
const imgDataBlobList = await Promise.all(svgURLs.map(imgToBlob))
|
||||
|
||||
const worker = new PDFWorkerHelper()
|
||||
const pdfArrayBuffer = await worker.generatePDF(imgDataBlobList, width, height)
|
||||
const pdfArrayBuffer = await worker.generatePDF(imgURLs, imgType, width, height)
|
||||
worker.terminate()
|
||||
|
||||
pdfBlob = new Blob([pdfArrayBuffer])
|
||||
|
@ -91,7 +55,7 @@ const getTitle = (scorePlayerData: ScorePlayerData) => {
|
|||
}
|
||||
|
||||
const getScoreFileName = (scorePlayerData: ScorePlayerData) => {
|
||||
return getTitle(scorePlayerData).replace(/[\s<>:"/\\|?*~\0\cA-\cZ]+/g, "_")
|
||||
return getTitle(scorePlayerData).replace(/[\s<>:{}"/\\|?*~.\0\cA-\cZ]+/g, "_")
|
||||
}
|
||||
|
||||
const main = () => {
|
||||
|
@ -119,7 +83,7 @@ const main = () => {
|
|||
|
||||
const imgType = getImgType() || "svg"
|
||||
|
||||
const svgURLs = Array.from({ length: getPagesNumber(scorePlayer) }).fill(null).map((_, i) => {
|
||||
const sheetImgURLs = Array.from({ length: getPagesNumber(scorePlayer) }).fill(null).map((_, i) => {
|
||||
return baseURL + `score_${i}.${imgType}`
|
||||
})
|
||||
|
||||
|
@ -166,7 +130,7 @@ const main = () => {
|
|||
|
||||
textNode.textContent = "Processing…"
|
||||
|
||||
generatePDF(svgURLs, filename).then(() => {
|
||||
generatePDF(sheetImgURLs, imgType, filename).then(() => {
|
||||
textNode.textContent = text
|
||||
})
|
||||
}
|
||||
|
|
|
@ -13,9 +13,10 @@ export class PDFWorkerHelper extends Worker {
|
|||
super(url)
|
||||
}
|
||||
|
||||
generatePDF(imgDataBlobList: Blob[], width: number, height: number): Promise<ArrayBuffer> {
|
||||
generatePDF(imgURLs: string[], imgType: "svg" | "png", width: number, height: number): Promise<ArrayBuffer> {
|
||||
const msg: PDFWorkerMessage = [
|
||||
imgDataBlobList,
|
||||
imgURLs,
|
||||
imgType,
|
||||
width,
|
||||
height,
|
||||
]
|
||||
|
|
|
@ -2,8 +2,11 @@
|
|||
/// <reference lib="webworker" />
|
||||
|
||||
import PDFDocument from "pdfkit/lib/document"
|
||||
import SVGtoPDF from "svg-to-pdfkit"
|
||||
|
||||
const generatePDF = async (imgDataUrlList: string[], width: number, height: number): Promise<ArrayBuffer> => {
|
||||
type ImgType = "svg" | "png"
|
||||
|
||||
const generatePDF = async (imgURLs: string[], imgType: ImgType, width: number, height: number): Promise<ArrayBuffer> => {
|
||||
|
||||
// @ts-ignore
|
||||
const pdf = new (PDFDocument as typeof import("pdfkit"))({
|
||||
|
@ -14,13 +17,26 @@ const generatePDF = async (imgDataUrlList: string[], width: number, height: numb
|
|||
layout: "portrait",
|
||||
})
|
||||
|
||||
imgDataUrlList.forEach((data) => {
|
||||
pdf.addPage()
|
||||
pdf.image(data, {
|
||||
width,
|
||||
height,
|
||||
if (imgType == "png") {
|
||||
const imgDataUrlList: string[] = await Promise.all(imgURLs.map(fetchDataURL))
|
||||
|
||||
imgDataUrlList.forEach((data) => {
|
||||
pdf.addPage()
|
||||
pdf.image(data, {
|
||||
width,
|
||||
height,
|
||||
})
|
||||
})
|
||||
})
|
||||
} else { // imgType == "svg"
|
||||
const svgList = await Promise.all(imgURLs.map(fetchText))
|
||||
|
||||
svgList.forEach((svg) => {
|
||||
pdf.addPage()
|
||||
SVGtoPDF(pdf, svg, 0, 0, {
|
||||
preserveAspectRatio: "none",
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const buf: Uint8Array = await pdf.getBuffer()
|
||||
|
@ -40,19 +56,30 @@ const getDataURL = (blob: Blob): Promise<string> => {
|
|||
})
|
||||
}
|
||||
|
||||
export type PDFWorkerMessage = [Blob[], number, number];
|
||||
const fetchDataURL = async (imgUrl: string): Promise<string> => {
|
||||
const r = await fetch(imgUrl)
|
||||
const blob = await r.blob()
|
||||
return getDataURL(blob)
|
||||
}
|
||||
|
||||
const fetchText = async (imgUrl: string): Promise<string> => {
|
||||
const r = await fetch(imgUrl)
|
||||
return r.text()
|
||||
}
|
||||
|
||||
export type PDFWorkerMessage = [string[], ImgType, number, number];
|
||||
|
||||
onmessage = async (e) => {
|
||||
const [
|
||||
imgDataBlobList,
|
||||
imgURLs,
|
||||
imgType,
|
||||
width,
|
||||
height,
|
||||
] = e.data as PDFWorkerMessage
|
||||
|
||||
const dataURLs = await Promise.all(imgDataBlobList.map(getDataURL))
|
||||
|
||||
const pdfBuf = await generatePDF(
|
||||
dataURLs,
|
||||
imgURLs,
|
||||
imgType,
|
||||
width,
|
||||
height,
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue