fix .ttc import bug
This commit is contained in:
parent
a37fd632a7
commit
a59e167307
7 changed files with 1982 additions and 221 deletions
|
@ -28,4 +28,4 @@ License: MIT
|
||||||
- 20231010: 初步完成tsit ê階段ê Parser`。
|
- 20231010: 初步完成tsit ê階段ê Parser`。
|
||||||
- 20231012: clo->js converter successfully (maybe.)
|
- 20231012: clo->js converter successfully (maybe.)
|
||||||
- 20231016:basic font guessing and `putText` function
|
- 20231016:basic font guessing and `putText` function
|
||||||
- issues : the CJK font can't be in .ttc format. (upstream-problem)
|
- 20231023-24:fix .ttc bug.
|
2035
package-lock.json
generated
2035
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -23,6 +23,7 @@
|
||||||
"@types/chai": "^4.3.5",
|
"@types/chai": "^4.3.5",
|
||||||
"@types/mocha": "^10.0.1",
|
"@types/mocha": "^10.0.1",
|
||||||
"@types/node": "^20.8.4",
|
"@types/node": "^20.8.4",
|
||||||
|
"@types/pdfkit": "^0.13.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.5.0",
|
"@typescript-eslint/eslint-plugin": "^6.5.0",
|
||||||
"chai": "^4.3.8",
|
"chai": "^4.3.8",
|
||||||
"eslint": "^8.48.0",
|
"eslint": "^8.48.0",
|
||||||
|
@ -37,12 +38,10 @@
|
||||||
"typescript": "^5.2.2"
|
"typescript": "^5.2.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@pdf-lib/fontkit": "^1.1.1",
|
|
||||||
"harfbuzzjs": "^0.3.3",
|
"harfbuzzjs": "^0.3.3",
|
||||||
"js-tokens": "^8.0.2",
|
|
||||||
"minimist": "^1.2.8",
|
"minimist": "^1.2.8",
|
||||||
"npx": "^10.2.2",
|
"npx": "^10.2.2",
|
||||||
"pdf-lib": "^1.17.1",
|
"pdfkit": "^0.13.0",
|
||||||
"typescript-parsec": "^0.3.4"
|
"typescript-parsec": "^0.3.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
109
src/canva.ts
109
src/canva.ts
|
@ -1,7 +1,6 @@
|
||||||
const { execSync } = require('child_process');
|
const { execSync } = require('child_process');
|
||||||
import { PDFDocument, RGB, ColorTypes } from "pdf-lib";
|
|
||||||
import { readFileSync, writeFileSync } from "fs";
|
import { readFileSync, writeFileSync } from "fs";
|
||||||
import fontkit from '@pdf-lib/fontkit';
|
import "pdfkit";
|
||||||
|
|
||||||
|
|
||||||
export interface CloCommand {
|
export interface CloCommand {
|
||||||
|
@ -9,27 +8,28 @@ export interface CloCommand {
|
||||||
args : TextStreamUnit[],
|
args : TextStreamUnit[],
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TextStreamUnit = string | CloCommand;
|
|
||||||
|
|
||||||
|
export type TextStreamUnit = string | CloCommand;
|
||||||
|
export type PDFDocument = PDFKit.PDFDocument;
|
||||||
/**
|
/**
|
||||||
* a clo document
|
* a clo document
|
||||||
*/
|
*/
|
||||||
export interface Clo{
|
export interface Clo{
|
||||||
mainText : TextStreamUnit[],
|
mainText : TextStreamUnit[],
|
||||||
mainFontStyle? : FontStyle,
|
mainFontStyle? : FontStyle,
|
||||||
PDFCanvas : PDFDocument,
|
PDFCanvas : PDFDocument;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Font Style Interface
|
* Font Style Interface
|
||||||
* name : eg. "FreeSans"
|
* family : eg. "FreeSans"
|
||||||
* size : in px, not in pt.
|
* size : in px, not in pt.
|
||||||
* textWeight : TextWeight.REGULAR ,etc
|
* textWeight : TextWeight.REGULAR ,etc
|
||||||
* textWeight : TextStyle.ITALIC ,etc
|
* textWeight : TextStyle.ITALIC ,etc
|
||||||
*/
|
*/
|
||||||
export interface FontStyle{
|
export interface FontStyle{
|
||||||
name : string,
|
family : string,
|
||||||
size : number,
|
size : number,
|
||||||
textWeight : TextWeight,
|
textWeight : TextWeight,
|
||||||
textStyle : TextStyle,
|
textStyle : TextStyle,
|
||||||
|
@ -47,59 +47,41 @@ export enum TextStyle{
|
||||||
OBLIQUE,
|
OBLIQUE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface fontPathPSNamePair{
|
||||||
|
path : string,
|
||||||
|
psName : string,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* guess the font path of a font style with fontconfig's commands
|
* guess the font path and postscript name of a font style with fontconfig's commands
|
||||||
* @param style the font style
|
* @param style the font style
|
||||||
* @returns the font path in string, if found none or .ttc, return a empty string.
|
* @returns pair of the font path and postscript name.
|
||||||
*/
|
*/
|
||||||
export function fontStyleTofontPath(style : FontStyle) : string{
|
export function fontStyleTofont(style : FontStyle) : fontPathPSNamePair{
|
||||||
try {
|
try {
|
||||||
let fcMatchOut = execSync(
|
let fcMatchCommand = `fc-match "${style.family}":${TextWeight[style.textWeight]}:`+
|
||||||
`fc-match "${style.name}":${TextWeight[style.textWeight]}:`+
|
`${TextStyle[style.textStyle]}` +` postscriptname file`;
|
||||||
`${TextStyle[style.textStyle]}`);
|
|
||||||
|
|
||||||
let fontFileName : string = fcMatchOut.toString().match(/^[^:]+/g)[0];
|
let fcMatchOut = execSync(fcMatchCommand);
|
||||||
|
let matched = fcMatchOut
|
||||||
|
.toString()
|
||||||
|
.match(/\:file=(.+):postscriptname=(.+)\n/);
|
||||||
|
|
||||||
|
let fontPath : string = matched[1];
|
||||||
|
let psName : string = matched[2];
|
||||||
|
|
||||||
|
return {path: fontPath, psName : psName};
|
||||||
|
|
||||||
if (fontFileName.match(/[.]ttc$/g)){
|
|
||||||
console.log("WARNING: the program doesn't support .ttc font format!\n"+
|
|
||||||
"Font file name: "+
|
|
||||||
fontFileName);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
let fcListOut = execSync(
|
|
||||||
`fc-list | grep ${fontFileName}`);
|
|
||||||
let fontPath : string = fcListOut.toString().match(/^[^:]+/g)[0];
|
|
||||||
return fontPath;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("WARNING: You should install `fontconfig` to select the font.");
|
console.log(`WARNING: You should install "fontconfig" to select the font.
|
||||||
return "";
|
Detail of the error:` + error);
|
||||||
|
|
||||||
|
return {path: "", psName : ""};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export function hexToRGB(hex : string) : RGB{
|
|
||||||
let matched = hex.match(/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/);
|
|
||||||
|
|
||||||
var result : RGB ;
|
|
||||||
if (!matched){
|
|
||||||
console.log("WARNING: the hex color code is not valid. set to #000000")
|
|
||||||
result = {
|
|
||||||
type : ColorTypes.RGB,
|
|
||||||
red: 0,
|
|
||||||
green: 0,
|
|
||||||
blue: 0,
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
result = {
|
|
||||||
type : ColorTypes.RGB,
|
|
||||||
red: parseInt(matched[1], 16),
|
|
||||||
green: parseInt(matched[2], 16),
|
|
||||||
blue: parseInt(matched[3], 16)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* put text in a clo canva.
|
* put text in a clo canva.
|
||||||
|
@ -114,28 +96,25 @@ export function hexToRGB(hex : string) : RGB{
|
||||||
export async function putText(clo : Clo, str : string, sty : FontStyle,
|
export async function putText(clo : Clo, str : string, sty : FontStyle,
|
||||||
pageNo : number, x : number, y : number): Promise<Clo>{
|
pageNo : number, x : number, y : number): Promise<Clo>{
|
||||||
|
|
||||||
clo.PDFCanvas.registerFontkit(fontkit);
|
let fontInfo = fontStyleTofont(sty);
|
||||||
let canvaPage = clo.PDFCanvas.getPage(pageNo);
|
|
||||||
|
|
||||||
|
if (fontInfo.path.match(/\.ttc$/g)){
|
||||||
const fontBytes = readFileSync(fontStyleTofontPath(sty));
|
var middle = clo.PDFCanvas
|
||||||
const fontEmbed = await clo.PDFCanvas.embedFont(fontBytes);
|
.font(fontInfo.path, fontInfo.psName)
|
||||||
|
.fontSize(sty.size);}
|
||||||
var textColor : RGB;
|
else{
|
||||||
if (sty.color === undefined){
|
var middle = clo.PDFCanvas
|
||||||
textColor = hexToRGB("#000000");
|
.font(fontInfo.path)
|
||||||
}else{
|
.fontSize(sty.size);
|
||||||
textColor = hexToRGB(sty.color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let drawTextOptions = {
|
if (sty.color !== undefined){
|
||||||
x : x,
|
middle.fill(sty.color);
|
||||||
y : canvaPage.getHeight() - y,
|
}
|
||||||
font : fontEmbed,
|
|
||||||
size : sty.size,
|
|
||||||
color : textColor};
|
|
||||||
|
|
||||||
canvaPage.drawText(str, drawTextOptions);
|
middle.text(str, x, y);
|
||||||
|
|
||||||
|
clo.PDFCanvas = middle;
|
||||||
|
|
||||||
return clo;
|
return clo;
|
||||||
};
|
};
|
|
@ -12,7 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.pdfGenerate = void 0;
|
exports.pdfGenerate = void 0;
|
||||||
const fs_1 = require("fs");
|
const fs_1 = require("fs");
|
||||||
const pdf_lib_1 = require("pdf-lib");
|
const pdf_lib_1 = require("pdf-lib");
|
||||||
var fontkit = require('@pdf-lib/fontkit');
|
var fontkit = require('pdf-fontkit');
|
||||||
function pdfGenerate() {
|
function pdfGenerate() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const pdfDoc = yield pdf_lib_1.PDFDocument.create();
|
const pdfDoc = yield pdf_lib_1.PDFDocument.create();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { readFileSync, writeFileSync } from "fs";
|
import { readFileSync, writeFileSync } from "fs";
|
||||||
import { PDFDocument } from "pdf-lib";
|
import { PDFDocument } from "pdf-lib";
|
||||||
var fontkit = require('@pdf-lib/fontkit');
|
var fontkit = require('pdf-fontkit');
|
||||||
|
|
||||||
export async function pdfGenerate(){
|
export async function pdfGenerate(){
|
||||||
|
|
||||||
|
|
|
@ -1,35 +1,51 @@
|
||||||
import * as canva from "../src/canva.js";
|
import * as canva from "../src/canva.js";
|
||||||
import { PDFDocument } from "pdf-lib";
|
import {createWriteStream} from 'fs';
|
||||||
var fontkit = require('@pdf-lib/fontkit');
|
import PDFDocument from 'pdfkit';
|
||||||
import {writeFileSync} from 'fs';
|
|
||||||
|
|
||||||
let hanziFont = {
|
let hanziFont = {
|
||||||
name : "思源黑體",
|
family : "Noto Sans CJK TC",
|
||||||
size : 12,
|
size : 12,
|
||||||
|
textWeight : canva.TextWeight.REGULAR,
|
||||||
|
textStyle : canva.TextStyle.ITALIC,
|
||||||
|
}
|
||||||
|
|
||||||
|
let romanFont = {
|
||||||
|
family : "FreeSans",
|
||||||
|
size : 15,
|
||||||
textWeight : canva.TextWeight.BOLD,
|
textWeight : canva.TextWeight.BOLD,
|
||||||
textStyle : canva.TextStyle.ITALIC,
|
textStyle : canva.TextStyle.ITALIC,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let arabicFont = {
|
||||||
|
family : "noto sans arabic",
|
||||||
|
size : 16,
|
||||||
|
textWeight : canva.TextWeight.REGULAR,
|
||||||
|
textStyle : canva.TextStyle.NORMAL,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async function foo (){
|
async function foo (){
|
||||||
|
|
||||||
let c = await PDFDocument.create();
|
const doc = new PDFDocument();
|
||||||
|
|
||||||
|
|
||||||
let clo = await {
|
let clo = await {
|
||||||
mainText : ["123"],
|
mainText : ["123 一隻貓跑過來"],
|
||||||
mainFontStyle : hanziFont,
|
mainFontStyle : hanziFont,
|
||||||
PDFCanvas : c,
|
PDFCanvas : doc,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
clo.PDFCanvas.pipe(createWriteStream('/tmp/output.pdf'));
|
||||||
|
|
||||||
|
|
||||||
clo.PDFCanvas.registerFontkit(fontkit);
|
|
||||||
const page = clo.PDFCanvas.addPage();
|
|
||||||
|
|
||||||
await canva.putText(clo, clo.mainText[0],hanziFont, 0, 100, 200);
|
await canva.putText(clo, clo.mainText[0],hanziFont, 0, 100, 200);
|
||||||
|
await canva.putText(clo, "ag téastáil" ,romanFont, 0, 100, 300);
|
||||||
|
await canva.putText(clo, "اَلْعَرَبِيَّةُ" ,arabicFont, 0, 100, 350);
|
||||||
|
|
||||||
const pdfBytes = await clo.PDFCanvas.save();
|
|
||||||
|
|
||||||
writeFileSync('/tmp/test.pdf', pdfBytes);
|
doc.end();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue