chore: remove unused assets and polyfills, add puppeteer for CMYK export automation
This commit is contained in:
1 parent
37971056c2
commit
8ad99a971d
15 files changed
+918
-6
No files matched your search
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
# production
|
# production
|
||||||
/build
|
/build
|
||||||
|
/dist
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
@@ -0,0 +1,134 @@
|
|||||||
|
import { execSync } from "node:child_process";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import os from "node:os";
|
||||||
|
import path from "node:path";
|
||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import puppeteer from "puppeteer";
|
||||||
|
|
||||||
|
export const runtime = "nodejs";
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
|
export async function GET(request) {
|
||||||
|
const { searchParams } = new URL(request.url);
|
||||||
|
const modulo = searchParams.get("modulo") || "1";
|
||||||
|
|
||||||
|
const publicDir = path.resolve(process.cwd(), "public/formacoes", modulo);
|
||||||
|
const htmlFile = path.join(publicDir, "index.html");
|
||||||
|
|
||||||
|
if (!fs.existsSync(htmlFile)) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: `Módulo ${modulo} não encontrado.` },
|
||||||
|
{ status: 404 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tmpDir = os.tmpdir();
|
||||||
|
const tmpRgbPdf = path.join(tmpDir, `espdocs-mod-${modulo}-${Date.now()}-rgb.pdf`);
|
||||||
|
const tmpCmykPdf = path.join(tmpDir, `espdocs-mod-${modulo}-${Date.now()}-cmyk.pdf`);
|
||||||
|
|
||||||
|
let browser = null;
|
||||||
|
try {
|
||||||
|
browser = await puppeteer.launch({
|
||||||
|
headless: "new",
|
||||||
|
args: [
|
||||||
|
"--no-sandbox",
|
||||||
|
"--disable-setuid-sandbox",
|
||||||
|
"--disable-web-security",
|
||||||
|
"--allow-file-access-from-files"
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const page = await browser.newPage();
|
||||||
|
await page.setViewport({ width: 1200, height: 1600, deviceScaleFactor: 2 });
|
||||||
|
await page.goto(`file://${htmlFile}`, { waitUntil: "networkidle0", timeout: 60000 });
|
||||||
|
|
||||||
|
// Aguarda o Paged.js concluir a montagem
|
||||||
|
await page.waitForFunction(() => {
|
||||||
|
const pages = document.querySelectorAll(".pagedjs_page");
|
||||||
|
return pages.length > 0 && !document.querySelector(".aviso-carregando");
|
||||||
|
}, { timeout: 30000 });
|
||||||
|
|
||||||
|
await new Promise((r) => setTimeout(r, 1000));
|
||||||
|
|
||||||
|
await page.pdf({
|
||||||
|
path: tmpRgbPdf,
|
||||||
|
format: "A4",
|
||||||
|
printBackground: true,
|
||||||
|
preferCSSPageSize: true,
|
||||||
|
margin: { top: 0, right: 0, bottom: 0, left: 0 }
|
||||||
|
});
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
|
browser = null;
|
||||||
|
|
||||||
|
// Converte para CMYK (DeviceCMYK / ISO Coated)
|
||||||
|
const gsCmd = `gs -dSAFER -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sColorConversionStrategy=CMYK -dProcessColorModel=/DeviceCMYK -dCompatibilityLevel=1.4 -sOutputFile="${tmpCmykPdf}" "${tmpRgbPdf}"`;
|
||||||
|
execSync(gsCmd, { stdio: "pipe" });
|
||||||
|
|
||||||
|
const pdfBuffer = fs.readFileSync(tmpCmykPdf);
|
||||||
|
|
||||||
|
return new NextResponse(pdfBuffer, {
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/pdf",
|
||||||
|
"Content-Disposition": `attachment; filename="LASER-ESP32-Modulo-${modulo}-CMYK.pdf"`,
|
||||||
|
"Content-Length": pdfBuffer.length.toString()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erro na conversão CMYK:", error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Falha ao gerar PDF CMYK", details: error.message },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
if (browser) await browser.close();
|
||||||
|
if (fs.existsSync(tmpRgbPdf)) fs.unlinkSync(tmpRgbPdf);
|
||||||
|
if (fs.existsSync(tmpCmykPdf)) fs.unlinkSync(tmpCmykPdf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(request) {
|
||||||
|
try {
|
||||||
|
const formData = await request.formData();
|
||||||
|
const file = formData.get("pdf");
|
||||||
|
|
||||||
|
if (!file) {
|
||||||
|
return NextResponse.json({ error: "Nenhum arquivo PDF enviado." }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const bytes = await file.arrayBuffer();
|
||||||
|
const buffer = Buffer.from(bytes);
|
||||||
|
|
||||||
|
const tmpDir = os.tmpdir();
|
||||||
|
const inputPath = path.join(tmpDir, `input-${Date.now()}.pdf`);
|
||||||
|
const outputPath = path.join(tmpDir, `cmyk-${Date.now()}.pdf`);
|
||||||
|
|
||||||
|
fs.writeFileSync(inputPath, buffer);
|
||||||
|
|
||||||
|
// Converte com Ghostscript
|
||||||
|
const gsCmd = `gs -dSAFER -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sColorConversionStrategy=CMYK -dProcessColorModel=/DeviceCMYK -dCompatibilityLevel=1.4 -sOutputFile="${outputPath}" "${inputPath}"`;
|
||||||
|
execSync(gsCmd, { stdio: "pipe" });
|
||||||
|
|
||||||
|
const outputBuffer = fs.readFileSync(outputPath);
|
||||||
|
|
||||||
|
// Limpa temporários
|
||||||
|
if (fs.existsSync(inputPath)) fs.unlinkSync(inputPath);
|
||||||
|
if (fs.existsSync(outputPath)) fs.unlinkSync(outputPath);
|
||||||
|
|
||||||
|
return new NextResponse(outputBuffer, {
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/pdf",
|
||||||
|
"Content-Disposition": `attachment; filename="documento-CMYK.pdf"`,
|
||||||
|
"Content-Length": outputBuffer.length.toString()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erro ao converter PDF POST:", error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Erro ao converter PDF para CMYK", details: error.message },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+329
@@ -25,6 +25,7 @@
|
|||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "15.5.6",
|
"eslint-config-next": "15.5.6",
|
||||||
|
"puppeteer": "^25.8.0",
|
||||||
"tailwindcss": "^4"
|
"tailwindcss": "^4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1542,6 +1543,35 @@
|
|||||||
"url": "https://opencollective.com/popperjs"
|
"url": "https://opencollective.com/popperjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@puppeteer/browsers": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-KDz+3qDRdBAlRlMjmKyj6dEs33YHTk/xRHEENSXq6TNnhgoU15ruSHtEBeVF6OZ9tBDY55Se4P0nFMNsipzU9A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"modern-tar": "^0.8.0",
|
||||||
|
"yargs": "^18.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"browsers": "lib/main-cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22.12.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"proxy-agent": ">=8.0.1",
|
||||||
|
"yauzl": "^2.10.0 || ^3.4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"proxy-agent": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"yauzl": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rtsao/scc": {
|
"node_modules/@rtsao/scc": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
||||||
@@ -2904,12 +2934,100 @@
|
|||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chromium-bidi": {
|
||||||
|
"version": "17.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-17.0.2.tgz",
|
||||||
|
"integrity": "sha512-5v9GQFhTktFvotn/OFNJBmKLKRAb6n9r0bVCwf7sHgWc3/JryK0bj1nn93L3pHFrfgcsu6Be6EWsDi+1XHTGDg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"mitt": "^3.0.1",
|
||||||
|
"zod": "^3.24.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.19.0 <22.0.0 || >=22.12.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"devtools-protocol": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/client-only": {
|
"node_modules/client-only": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
||||||
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
|
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/cliui": {
|
||||||
|
"version": "9.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
|
||||||
|
"integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^7.2.0",
|
||||||
|
"strip-ansi": "^7.1.0",
|
||||||
|
"wrap-ansi": "^9.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cliui/node_modules/ansi-styles": {
|
||||||
|
"version": "6.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
|
||||||
|
"integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cliui/node_modules/emoji-regex": {
|
||||||
|
"version": "10.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
|
||||||
|
"integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/cliui/node_modules/string-width": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"emoji-regex": "^10.3.0",
|
||||||
|
"get-east-asian-width": "^1.0.0",
|
||||||
|
"strip-ansi": "^7.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cliui/node_modules/wrap-ansi": {
|
||||||
|
"version": "9.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
|
||||||
|
"integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^6.2.1",
|
||||||
|
"string-width": "^7.0.0",
|
||||||
|
"strip-ansi": "^7.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/clsx": {
|
"node_modules/clsx": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||||
@@ -3126,6 +3244,13 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/devtools-protocol": {
|
||||||
|
"version": "0.0.1666840",
|
||||||
|
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1666840.tgz",
|
||||||
|
"integrity": "sha512-gCcO42XCHKEs7Ag0S7aGYsnJ7hlgrO3qderYqeiY0Eqk+0GFfuvT13IA0hHreJTa2KCdDVyGMeOhdMNmrrTjVg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/doctrine": {
|
"node_modules/doctrine": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
|
||||||
@@ -3381,6 +3506,16 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/escalade": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/escape-string-regexp": {
|
"node_modules/escape-string-regexp": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||||
@@ -4128,6 +4263,29 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/get-caller-file": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": "6.* || 8.* || >= 10.*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-east-asian-width": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/get-intrinsic": {
|
"node_modules/get-intrinsic": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||||
@@ -5436,6 +5594,19 @@
|
|||||||
"url": "https://opencollective.com/parcel"
|
"url": "https://opencollective.com/parcel"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lilconfig": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antonk52"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lines-and-columns": {
|
"node_modules/lines-and-columns": {
|
||||||
"version": "1.2.4",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||||
@@ -5567,6 +5738,23 @@
|
|||||||
"node": ">=16 || 14 >=14.17"
|
"node": ">=16 || 14 >=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mitt": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/modern-tar": {
|
||||||
|
"version": "0.8.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.8.4.tgz",
|
||||||
|
"integrity": "sha512-gN54ddmyzEg10orwZ2u4OOv+bjpMWdIl5jIkodK97bMq8QBSL5c0D7YX0lT1Ooz+99S7+PvFbnxzdjgHo1r41g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/motion-dom": {
|
"node_modules/motion-dom": {
|
||||||
"version": "12.40.0",
|
"version": "12.40.0",
|
||||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.40.0.tgz",
|
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.40.0.tgz",
|
||||||
@@ -6127,6 +6315,46 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/puppeteer": {
|
||||||
|
"version": "25.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-25.8.0.tgz",
|
||||||
|
"integrity": "sha512-3gcUJ+Jfodb5zNa/lWLZukBUwYiRIwAc8WRICoqfi+ZYmNWqpsPFyanTU3Gw/lhgII9aotVbAmhT6/NKHWgyUA==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@puppeteer/browsers": "3.2.1",
|
||||||
|
"chromium-bidi": "17.0.2",
|
||||||
|
"devtools-protocol": "0.0.1666840",
|
||||||
|
"lilconfig": "^3.1.3",
|
||||||
|
"puppeteer-core": "25.8.0",
|
||||||
|
"typed-query-selector": "^2.12.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"puppeteer": "lib/puppeteer/node/cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/puppeteer-core": {
|
||||||
|
"version": "25.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-25.8.0.tgz",
|
||||||
|
"integrity": "sha512-LDOrawV8vfCVk+yLj2ozvajNP4Sv3OV9y3Tpiyy2g2Z+aQlbcozP6KJfI4iSBq7YQER+86ihEtPa5ioiZyWxMQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@puppeteer/browsers": "3.2.1",
|
||||||
|
"chromium-bidi": "17.0.2",
|
||||||
|
"devtools-protocol": "0.0.1666840",
|
||||||
|
"typed-query-selector": "^2.12.2",
|
||||||
|
"webdriver-bidi-protocol": "0.4.2",
|
||||||
|
"ws": "^8.21.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.14.1",
|
"version": "6.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
|
||||||
@@ -7162,6 +7390,13 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/typed-query-selector": {
|
||||||
|
"version": "2.12.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.2.tgz",
|
||||||
|
"integrity": "sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.9.3",
|
"version": "5.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
@@ -7256,6 +7491,13 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/webdriver-bidi-protocol": {
|
||||||
|
"version": "0.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.2.tgz",
|
||||||
|
"integrity": "sha512-VSV+fzfChirL3e7jay2yUC7B4HQCGtEWEg/MSSQbK+qWbqeGlRLlXTzPpYr3XGUvbpDHumWZBJxgesg4N7dbtA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
@@ -7458,6 +7700,38 @@
|
|||||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ws": {
|
||||||
|
"version": "8.21.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz",
|
||||||
|
"integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": ">=5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/y18n": {
|
||||||
|
"version": "5.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||||
|
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yaml": {
|
"node_modules/yaml": {
|
||||||
"version": "1.10.2",
|
"version": "1.10.2",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||||
@@ -7467,6 +7741,51 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/yargs": {
|
||||||
|
"version": "18.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs/-/yargs-18.1.0.tgz",
|
||||||
|
"integrity": "sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cliui": "^9.0.1",
|
||||||
|
"escalade": "^3.1.1",
|
||||||
|
"get-caller-file": "^2.0.5",
|
||||||
|
"string-width": "^8.2.1",
|
||||||
|
"y18n": "^5.0.5",
|
||||||
|
"yargs-parser": "^22.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || ^22.12.0 || >=23"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yargs-parser": {
|
||||||
|
"version": "22.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
|
||||||
|
"integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || ^22.12.0 || >=23"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yargs/node_modules/string-width": {
|
||||||
|
"version": "8.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz",
|
||||||
|
"integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"get-east-asian-width": "^1.5.0",
|
||||||
|
"strip-ansi": "^7.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yocto-queue": {
|
"node_modules/yocto-queue": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||||
@@ -7479,6 +7798,16 @@
|
|||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zod": {
|
||||||
|
"version": "3.25.76",
|
||||||
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||||
|
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3
-1
@@ -7,7 +7,8 @@
|
|||||||
"build": "next build --turbopack",
|
"build": "next build --turbopack",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "eslint",
|
"lint": "eslint",
|
||||||
"auditar-temas": "node scripts/auditar-temas.mjs"
|
"auditar-temas": "node scripts/auditar-temas.mjs",
|
||||||
|
"exportar-cmyk": "node scripts/exportar-cmyk.mjs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "15.5.6",
|
"eslint-config-next": "15.5.6",
|
||||||
|
"puppeteer": "^25.8.0",
|
||||||
"tailwindcss": "^4"
|
"tailwindcss": "^4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File renamed without changes.
File renamed without changes.
@@ -1960,12 +1960,147 @@ Temperatura: 26.1 C | Umidade: 62.0 %</code></pre>
|
|||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- ══════════════════════ PAINEL DE EXPORTAÇÃO ══════════════════════ -->
|
||||||
|
<div class="painel-exportacao" id="painelExportacao" style="position: fixed !important; top: 18px !important; right: 20px !important; z-index: 999999 !important; display: flex !important;">
|
||||||
|
<span class="painel-exportacao-titulo">
|
||||||
|
<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<polyline points="6 9 6 2 18 2 18 9"></polyline>
|
||||||
|
<path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"></path>
|
||||||
|
<rect x="6" y="14" width="12" height="8"></rect>
|
||||||
|
</svg>
|
||||||
|
Apostila:
|
||||||
|
</span>
|
||||||
|
<button type="button" class="btn-exportar btn-exportar-rgb" id="btnExportarRgb" title="Imprimir ou Salvar em PDF (padrão RGB do navegador)">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
|
||||||
|
<polyline points="14 2 14 8 20 8"></polyline>
|
||||||
|
<line x1="16" y1="13" x2="8" y2="13"></line>
|
||||||
|
<line x1="16" y1="17" x2="8" y2="17"></line>
|
||||||
|
<polyline points="10 9 9 9 8 9"></polyline>
|
||||||
|
</svg>
|
||||||
|
<span>Exportar PDF</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn-exportar btn-exportar-cmyk" id="btnExportarCmyk" title="Exportar PDF convertido para espaço de cor CMYK (ISO Coated) para gráfica">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="10"></circle>
|
||||||
|
<path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"></path>
|
||||||
|
<path d="M2 12h20"></path>
|
||||||
|
</svg>
|
||||||
|
<span>Exportar em CMYK</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="toast-exportacao" id="toastExportacao" style="position: fixed !important; bottom: 24px !important; right: 24px !important; z-index: 1000000 !important;"></div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Tira o aviso do fluxo antes da paginação começar, senão o Paged.js o
|
let paginacaoConcluida = false;
|
||||||
// trataria como conteúdo e ele apareceria impresso na primeira página.
|
const painelExportacao = document.getElementById("painelExportacao");
|
||||||
|
const toastExportacao = document.getElementById("toastExportacao");
|
||||||
|
|
||||||
|
function fixarPainelNaTela() {
|
||||||
|
if (painelExportacao) {
|
||||||
|
painelExportacao.style.cssText = "position: fixed !important; top: 18px !important; right: 20px !important; z-index: 999999 !important; display: flex !important;";
|
||||||
|
if (!document.body.contains(painelExportacao)) {
|
||||||
|
document.body.appendChild(painelExportacao);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (toastExportacao) {
|
||||||
|
toastExportacao.style.cssText = "position: fixed !important; bottom: 24px !important; right: 24px !important; z-index: 1000000 !important;";
|
||||||
|
if (!document.body.contains(toastExportacao)) {
|
||||||
|
document.body.appendChild(toastExportacao);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window.PagedConfig = {
|
window.PagedConfig = {
|
||||||
before: () => document.querySelector(".aviso-carregando")?.remove(),
|
before: () => {
|
||||||
|
document.querySelector(".aviso-carregando")?.remove();
|
||||||
|
// Remove do fluxo do DOM para o Paged.js não paginar como conteúdo
|
||||||
|
painelExportacao?.remove();
|
||||||
|
toastExportacao?.remove();
|
||||||
|
},
|
||||||
|
after: () => {
|
||||||
|
paginacaoConcluida = true;
|
||||||
|
document.body.classList.add("pagedjs-pronto");
|
||||||
|
fixarPainelNaTela();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Oculta painel na impressão nativa
|
||||||
|
window.addEventListener("beforeprint", () => {
|
||||||
|
if (painelExportacao) painelExportacao.style.setProperty("display", "none", "important");
|
||||||
|
if (toastExportacao) toastExportacao.style.setProperty("display", "none", "important");
|
||||||
|
});
|
||||||
|
window.addEventListener("afterprint", () => {
|
||||||
|
if (painelExportacao) painelExportacao.style.setProperty("display", "flex", "important");
|
||||||
|
});
|
||||||
|
|
||||||
|
function exibirToast(mensagem, tipo = "info", duracao = 4000) {
|
||||||
|
const toast = document.getElementById("toastExportacao") || toastExportacao;
|
||||||
|
if (!toast) return;
|
||||||
|
if (!document.body.contains(toast)) document.body.appendChild(toast);
|
||||||
|
toast.textContent = mensagem;
|
||||||
|
toast.className = `toast-exportacao ativo ${tipo}`;
|
||||||
|
clearTimeout(toast._timer);
|
||||||
|
toast._timer = setTimeout(() => {
|
||||||
|
toast.classList.remove("ativo");
|
||||||
|
}, duracao);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("click", async (e) => {
|
||||||
|
const btnRgb = e.target.closest("#btnExportarRgb");
|
||||||
|
const btnCmyk = e.target.closest("#btnExportarCmyk");
|
||||||
|
|
||||||
|
if (btnRgb) {
|
||||||
|
if (!paginacaoConcluida) {
|
||||||
|
exibirToast("Aguarde a paginação terminar antes de exportar...", "info", 2500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.print();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btnCmyk) {
|
||||||
|
const textoOriginal = btnCmyk.innerHTML;
|
||||||
|
|
||||||
|
try {
|
||||||
|
btnCmyk.disabled = true;
|
||||||
|
btnCmyk.classList.add("carregando");
|
||||||
|
btnCmyk.innerHTML = `<span class="spinner-exportar"></span><span>Convertendo CMYK...</span>`;
|
||||||
|
exibirToast("Gerando PDF e convertendo para CMYK (ISO Coated)... Aguarde alguns segundos.", "info", 8000);
|
||||||
|
|
||||||
|
const resposta = await fetch("/api/export-cmyk?modulo=1");
|
||||||
|
|
||||||
|
if (!resposta.ok) {
|
||||||
|
const erroJson = await resposta.json().catch(() => null);
|
||||||
|
throw new Error(erroJson?.error || "Serviço de conversão indisponível no momento.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const blob = await resposta.blob();
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.style.display = "none";
|
||||||
|
a.href = url;
|
||||||
|
a.download = "LASER-ESP32-Modulo-1-CMYK.pdf";
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
a.remove();
|
||||||
|
|
||||||
|
exibirToast("✅ PDF em CMYK gerado e baixado com sucesso!", "sucesso", 5000);
|
||||||
|
} catch (erro) {
|
||||||
|
console.warn("Erro ao gerar CMYK via API:", erro);
|
||||||
|
exibirToast(
|
||||||
|
"💡 Dica: execute 'npm run exportar-cmyk 1' no terminal para gerar o arquivo CMYK imediatamente!",
|
||||||
|
"erro",
|
||||||
|
7000
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
btnCmyk.disabled = false;
|
||||||
|
btnCmyk.classList.remove("carregando");
|
||||||
|
btnCmyk.innerHTML = textoOriginal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<script src="../assets/paged.polyfill.js"></script>
|
<script src="../assets/paged.polyfill.js"></script>
|
||||||
</body>
|
</body>
|
||||||
File renamed without changes.
File renamed without changes.
@@ -665,6 +665,237 @@ figcaption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media print {
|
/* -------------------------------------------------------------------------
|
||||||
.aviso-carregando { display: none; }
|
8. Barra Flutuante de Exportação e Impressão (Apenas em Tela)
|
||||||
|
------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
.painel-exportacao {
|
||||||
|
position: fixed !important;
|
||||||
|
top: 18px !important;
|
||||||
|
right: 20px !important;
|
||||||
|
z-index: 999999 !important;
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
background: rgba(6, 38, 57, 0.92);
|
||||||
|
backdrop-filter: blur(14px);
|
||||||
|
-webkit-backdrop-filter: blur(14px);
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-radius: 9999px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
box-shadow:
|
||||||
|
0 8px 30px rgba(0, 0, 0, 0.4),
|
||||||
|
0 0 0 1px rgba(0, 0, 0, 0.2);
|
||||||
|
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.painel-exportacao.oculto {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
transform: translateY(-8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.painel-exportacao-titulo {
|
||||||
|
font-family: "Inter", sans-serif;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #94a3b8;
|
||||||
|
padding: 0 4px 0 6px;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-exportar {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 6px 13px;
|
||||||
|
border-radius: 9999px;
|
||||||
|
font-family: "Inter", sans-serif;
|
||||||
|
font-size: 11.5px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.18s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
white-space: nowrap;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-exportar svg {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-exportar-rgb {
|
||||||
|
background: rgba(255, 255, 255, 0.12);
|
||||||
|
color: #f1f5f9;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-exportar-rgb:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
color: #ffffff;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-exportar-rgb:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-exportar-cmyk {
|
||||||
|
background: linear-gradient(135deg, #F77F00, #D62828);
|
||||||
|
color: #ffffff;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
box-shadow: 0 2px 10px rgba(214, 40, 40, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-exportar-cmyk:hover {
|
||||||
|
background: linear-gradient(135deg, #ff911a, #e63939);
|
||||||
|
box-shadow: 0 4px 14px rgba(214, 40, 40, 0.5);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-exportar-cmyk:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
box-shadow: 0 1px 4px rgba(214, 40, 40, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-exportar:disabled,
|
||||||
|
.btn-exportar.carregando {
|
||||||
|
opacity: 0.75;
|
||||||
|
cursor: wait;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner-exportar {
|
||||||
|
width: 13px;
|
||||||
|
height: 13px;
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.35);
|
||||||
|
border-top-color: #ffffff;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: girar-spinner 0.75s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes girar-spinner {
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toast de notificação de exportação */
|
||||||
|
.toast-exportacao {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 24px;
|
||||||
|
right: 24px;
|
||||||
|
z-index: 10001;
|
||||||
|
background: #062639;
|
||||||
|
color: #e2e8f0;
|
||||||
|
border: 1px solid #33566a;
|
||||||
|
padding: 10px 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-family: "Inter", sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.4);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(12px);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-exportacao.ativo {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-exportacao.sucesso { border-color: #10b981; }
|
||||||
|
.toast-exportacao.erro { border-color: #ef4444; }
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
9. Regras Especiais para Impressão Física e Geração de PDF (@media print)
|
||||||
|
------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
.aviso-carregando {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
BLOCO DE CÓDIGO - TEMA CLARO DE ALTO CONTRASTE
|
||||||
|
Elimina o fundo escuro pesado no papel e resolve o problema de
|
||||||
|
sangria de tinta (dot gain) que engolia as letras.
|
||||||
|
*/
|
||||||
|
pre {
|
||||||
|
background: #f8fafc !important;
|
||||||
|
border: 0.35mm solid #cbd5e1 !important;
|
||||||
|
color: #0f172a !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
-webkit-print-color-adjust: exact !important;
|
||||||
|
print-color-adjust: exact !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre code {
|
||||||
|
background: transparent !important;
|
||||||
|
border: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
color: #0f172a !important;
|
||||||
|
font-weight: 500 !important;
|
||||||
|
-webkit-print-color-adjust: exact !important;
|
||||||
|
print-color-adjust: exact !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre span {
|
||||||
|
background: transparent !important;
|
||||||
|
border: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cores de sintaxe de alta densidade e seguras para CMYK/P&B */
|
||||||
|
.c-com {
|
||||||
|
color: #475569 !important;
|
||||||
|
font-style: italic !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-key {
|
||||||
|
color: #6b21a8 !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-fun {
|
||||||
|
color: #0369a1 !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-str {
|
||||||
|
color: #15803d !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-num {
|
||||||
|
color: #b45309 !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-pre {
|
||||||
|
color: #be185d !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Código inline (apenas fora do pre) */
|
||||||
|
:not(pre) > code {
|
||||||
|
background: #f1f5f9 !important;
|
||||||
|
color: #9a3412 !important;
|
||||||
|
border: 0.2mm solid #cbd5e1 !important;
|
||||||
|
padding: 0.4mm 1.2mm !important;
|
||||||
|
border-radius: 1mm !important;
|
||||||
|
-webkit-print-color-adjust: exact !important;
|
||||||
|
print-color-adjust: exact !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,80 @@
|
|||||||
|
import { execSync } from "node:child_process";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
import puppeteer from "puppeteer";
|
||||||
|
|
||||||
|
const modulo = process.argv[2] || "1";
|
||||||
|
const publicDir = path.resolve("public/formacoes", modulo);
|
||||||
|
const htmlFile = path.join(publicDir, "index.html");
|
||||||
|
|
||||||
|
if (!fs.existsSync(htmlFile)) {
|
||||||
|
console.error(`❌ Erro: Apostila do módulo ${modulo} não encontrada em ${htmlFile}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const outputDir = path.resolve("dist/apostilas");
|
||||||
|
if (!fs.existsSync(outputDir)) {
|
||||||
|
fs.mkdirSync(outputDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
const tmpRgbPdf = path.join(outputDir, `modulo-${modulo}-rgb-temp.pdf`);
|
||||||
|
const finalCmykPdf = path.join(outputDir, `LASER-ESP32-Modulo-${modulo}-CMYK.pdf`);
|
||||||
|
|
||||||
|
console.log(`\n🚀 Iniciando exportação da Apostila (Módulo ${modulo})...`);
|
||||||
|
console.log(`📄 Arquivo fonte: ${htmlFile}`);
|
||||||
|
|
||||||
|
async function exportar() {
|
||||||
|
console.log("\n[1/3] Renderizando páginas no Puppeteer e aguardando Paged.js...");
|
||||||
|
const browser = await puppeteer.launch({
|
||||||
|
headless: "new",
|
||||||
|
args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-web-security", "--allow-file-access-from-files"]
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const page = await browser.newPage();
|
||||||
|
await page.setViewport({ width: 1200, height: 1600, deviceScaleFactor: 2 });
|
||||||
|
|
||||||
|
await page.goto(`file://${htmlFile}`, { waitUntil: "networkidle0", timeout: 60000 });
|
||||||
|
|
||||||
|
// Aguarda o Paged.js concluir a montagem de todas as páginas
|
||||||
|
await page.waitForFunction(() => {
|
||||||
|
const pages = document.querySelectorAll(".pagedjs_page");
|
||||||
|
return pages.length > 0 && !document.querySelector(".aviso-carregando");
|
||||||
|
}, { timeout: 30000 });
|
||||||
|
|
||||||
|
// Aguarda 1 segundo adicional para renderização de fontes e SVG
|
||||||
|
await new Promise((r) => setTimeout(r, 1000));
|
||||||
|
|
||||||
|
console.log("[2/3] Gerando PDF em alta resolução...");
|
||||||
|
await page.pdf({
|
||||||
|
path: tmpRgbPdf,
|
||||||
|
format: "A4",
|
||||||
|
printBackground: true,
|
||||||
|
preferCSSPageSize: true,
|
||||||
|
margin: { top: 0, right: 0, bottom: 0, left: 0 }
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`[3/3] Convertendo espaço de cor para CMYK (ISO Coated) via Ghostscript...`);
|
||||||
|
const gsCmd = `gs -dSAFER -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sColorConversionStrategy=CMYK -dProcessColorModel=/DeviceCMYK -dCompatibilityLevel=1.4 -sOutputFile="${finalCmykPdf}" "${tmpRgbPdf}"`;
|
||||||
|
|
||||||
|
execSync(gsCmd, { stdio: "pipe" });
|
||||||
|
|
||||||
|
if (fs.existsSync(tmpRgbPdf)) {
|
||||||
|
fs.unlinkSync(tmpRgbPdf);
|
||||||
|
}
|
||||||
|
|
||||||
|
const stats = fs.statSync(finalCmykPdf);
|
||||||
|
const tamanhoMB = (stats.size / (1024 * 1024)).toFixed(2);
|
||||||
|
|
||||||
|
console.log(`\n✅ PDF em CMYK gerado com sucesso!`);
|
||||||
|
console.log(`📂 Destino: ${finalCmykPdf}`);
|
||||||
|
console.log(`📊 Tamanho: ${tamanhoMB} MB\n`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("❌ Erro durante a geração:", err);
|
||||||
|
process.exit(1);
|
||||||
|
} finally {
|
||||||
|
await browser.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exportar();
|
||||||
Reference in new issue
Block a user