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…
	
	Add table
		Add a link
		
	
		Reference in a new issue