suneditor
Version:
Vanilla JavaScript based WYSIWYG web editor
147 lines (123 loc) • 4.46 kB
JavaScript
import { PluginCommand } from '../../interfaces';
import { dom, env } from '../../helper';
import { ApiManager } from '../../modules/manager';
const { _w, _d } = env;
/**
* @typedef ExportPDFPluginOptions
* @property {string} apiUrl - Server request URL for PDF generation
* @property {string} [fileName="suneditor-pdf"] - Name of the generated PDF file
*/
/**
* @class
* @description Export PDF plugin
*/
class ExportPDF extends PluginCommand {
static key = 'exportPDF';
static className = 'se-component-enabled';
/**
* @constructor
* @param {SunEditor.Kernel} kernel - The Kernel instance
* @param {ExportPDFPluginOptions} pluginOptions - plugin options
*/
constructor(kernel, pluginOptions) {
super(kernel);
// plugin basic properties
this.title = this.$.lang.exportPDF;
this.icon = 'PDF';
// plugin options
this.apiUrl = pluginOptions.apiUrl;
this.fileName = pluginOptions.fileName || 'suneditor-pdf';
// option check
if (!this.apiUrl) {
console.warn('[SUNEDITOR.plugins.exportPDF.error] Requires exportPDF."apiUrl" options.');
} else {
this.apiManager = new ApiManager(this, this.$, {
method: 'POST',
url: this.apiUrl,
headers: {
'Content-Type': 'application/json',
},
responseType: 'blob',
});
}
}
/**
* @override
* @type {PluginCommand['action']}
*/
async action() {
if (!this.apiUrl) {
console.warn('[SUNEDITOR.plugins.exportPDF.error] Requires exportPDF."apiUrl" options.');
return;
}
this.$.ui.showLoading();
let ww = null;
try {
const standardWW = this.$.frameContext.get('documentTypePageMirror') || this.$.frameContext.get('wysiwygFrame');
// Strip theme class so getComputedStyle resolves default (light) colors for borders, shadows, etc.
const themeClass = (this.$.options.get('_themeClass') || '').trim();
const wwClassName = themeClass ? standardWW.className.replace(themeClass, '').trim() : standardWW.className;
const editableDiv = dom.utils.createElement('div', { class: wwClassName }, standardWW.innerHTML);
ww = dom.utils.createElement('div', { style: `position: absolute; top: -10000px; left: -10000px; width: 21cm; columns: 21cm; height: auto;` }, editableDiv);
const innerPadding = _w.getComputedStyle(standardWW).padding;
const inlineWW = dom.utils.applyInlineStylesAll(editableDiv, true, this.$.options.get('allUsedStyles'));
inlineWW.style.padding = inlineWW.style.paddingTop = inlineWW.style.paddingBottom = inlineWW.style.paddingLeft = inlineWW.style.paddingRight = '0';
ww.innerHTML = `
<style>
{
size: A4;
margin: ${innerPadding};
}
</style>
${inlineWW.outerHTML}`;
_d.body.appendChild(ww);
// before event
if ((await this.$.eventManager.triggerEvent('onExportPDFBefore', { target: ww })) === false) return;
// at server
await this.#createByServer(ww);
return;
} catch (error) {
console.error('[SUNEDITOR.plugins.exportPDF.error]', error.message);
} finally {
dom.utils.removeItem(ww);
this.$.ui.hideLoading();
}
}
/**
* @description Sends the editor content to the server for PDF generation.
* @param {HTMLElement} ww - A temporary container holding the formatted editor content.
* @returns {Promise<void>} Resolves when the PDF file is successfully downloaded.
* @throws {Error} Throws an error if the server response indicates a failure.
*/
async #createByServer(ww) {
const data = {
fileName: this.fileName,
htmlContent: ww.innerHTML,
};
const xhr = await this.apiManager.asyncCall({ data: JSON.stringify(data) });
if (xhr.status !== 200) {
let errorMessage;
try {
errorMessage = JSON.parse(xhr.responseText).errorMessage;
} catch {
// ignore
}
throw Error(`[SUNEDITOR.plugins.exportPDF.error] ${errorMessage || xhr.statusText}`);
}
const blob = new Blob([xhr.response], { type: 'application/pdf' });
const contentDisposition = xhr.getResponseHeader('Content-Disposition');
const downloadUrl = URL.createObjectURL(blob);
const filename = (contentDisposition.match(/filename="([^"]+)/) || [])[1] || this.fileName + '.pdf';
const a = dom.utils.createElement('A', { href: downloadUrl, download: filename, style: 'display: none;' }, null);
try {
_d.body.appendChild(a);
a.click();
} finally {
_w.setTimeout(() => {
dom.utils.removeItem(a);
URL.revokeObjectURL(downloadUrl);
}, 100);
}
}
}
export default ExportPDF;