Skip to content

Commit

Permalink
2.8.0-rc-1, 0.17.6-28
Browse files Browse the repository at this point in the history
  • Loading branch information
zsviczian committed Jan 24, 2025
1 parent 23b94da commit b0bc034
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 53 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"license": "MIT",
"dependencies": {
"@popperjs/core": "^2.11.8",
"@zsviczian/excalidraw": "0.17.6-27",
"@zsviczian/excalidraw": "0.17.6-28",
"chroma-js": "^2.4.2",
"clsx": "^2.0.0",
"@zsviczian/colormaster": "^1.2.2",
Expand Down
3 changes: 1 addition & 2 deletions src/shared/Dialogs/ExportDialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,6 @@ export class ExportDialog extends Modal {
});
bPDFExport.onclick = () => {
this.view.exportPDF(
false,
this.hasSelectedElements && this.exportSelectedOnly,
this.pageSize,
this.pageOrientation
Expand All @@ -402,7 +401,7 @@ export class ExportDialog extends Modal {

public getPaperColor(): string {
switch (this.paperColor) {
case "white": return "#ffffff";
case "white": return this.theme === "light" ? "#ffffff" : "#000000";
case "scene": return this.api.getAppState().viewBackgroundColor;
case "custom": return this.customPaperColor;
default: return "#ffffff";
Expand Down
51 changes: 38 additions & 13 deletions src/shared/Dialogs/SuggesterInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,14 +226,14 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
},
{
field: "createPDF",
code: "async createPDF({SVG: SVGSVGElement[], scale?: PDFExportScale, pageProps?: PDFPageProperties}): Promise<void>",
desc: "",
after: "Creates a PDF from the provided SVG elements with specified scaling and page properties.\n" +
code: "async createPDF({SVG: SVGSVGElement[], scale?: PDFExportScale, pageProps?: PDFPageProperties, filename: string}): Promise<void>",
desc: "Creates a PDF from the provided SVG elements with specified scaling and page properties.\n" +
"\n" +
"@param {Object} params - The parameters for creating the PDF.\n" +
"@param {SVGSVGElement[]} params.SVG - An array of SVG elements to be included in the PDF. If multiple SVGs are provided, each will be added to a new page.\n" +
"@param {PDFExportScale} [params.scale={ fitToPage: true, zoom: 1 }] - The scaling options for the SVG elements.\n" +
"@param {PDFPageProperties} [params.pageProps] - The properties for the PDF pages.\n" +
"@param {string} params.filename - The name of the PDF file to be created.\n" +
"@returns {Promise<ArrayBuffer>} - A promise that resolves to an ArrayBuffer containing the PDF data.\n" +
"\n" +
"@typedef {Object} PDFExportScale\n" +
Expand All @@ -244,20 +244,49 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
"@property {{width: number, height: number}} [dimensions] - The dimensions of the PDF pages in pixels. Use getPageDimensions to get standard page sizes.\n" +
"@property {string} [backgroundColor] - The background color of the PDF pages.\n" +
"@property {PDFMargin} margin - The margins of the PDF pages in pixels.\n" +
"@property {PDFPageAlignment} alignment - The alignment of the SVG on the PDF pages.\n" +
"\n" +
"@example\n" +
"const pdfData = await createPDF({\n" +
"@property {PDFPageAlignment} alignment - The alignment of the SVG on the PDF pages.",
after: "({\n" +
" SVG: [svgElement1, svgElement2],\n" +
" scale: { fitToPage: true },\n" +
" pageProps: {\n" +
" dimensions: { width: 595.28, height: 841.89 },\n" +
" backgroundColor: \"#ffffff\",\n" +
" margin: { left: 20, right: 20, top: 20, bottom: 20 },\n" +
" alignment: \"center\"\n" +
" filename: \"myPDF.pdf\"\n" +
" }\n" +
"});",
},
{
field: "createViewSVG",
code: "async createViewSVG({withBackground?: boolean, theme?: 'light' | 'dark', frameRendering?: FrameRenderingOptions, padding?: number, selectedOnly?: boolean, skipInliningFonts?: boolean, embedScene?: boolean}): Promise<SVGSVGElement>",
desc: "Creates an SVG representation of the current view with specified options.\n" +
"\n" +
"@param {Object} options - The options for creating the SVG.\n" +
"@param {boolean} [options.withBackground=true] - Whether to include the background in the SVG.\n" +
"@param {\"light\" | \"dark\"} [options.theme] - The theme to use for the SVG.\n" +
"@param {FrameRenderingOptions} [options.frameRendering={enabled: true, name: true, outline: true, clip: true}] - The frame rendering options.\n" +
"@param {number} [options.padding] - The padding to apply around the SVG.\n" +
"@param {boolean} [options.selectedOnly=false] - Whether to include only the selected elements in the SVG.\n" +
"@param {boolean} [options.skipInliningFonts=false] - Whether to skip inlining fonts in the SVG.\n" +
"@param {boolean} [options.embedScene=false] - Whether to embed the scene in the SVG.\n" +
"@returns {Promise<SVGSVGElement>} A promise that resolves to the SVG element.\n" +
"\n" +
"@typedef {Object} FrameRenderingOptions\n" +
"@property {boolean} enabled - Whether frame rendering is enabled.\n" +
"@property {boolean} name - Whether to include the name in the frame rendering.\n" +
"@property {boolean} outline - Whether to include the outline in the frame rendering.\n" +
"@property {boolean} clip - Whether to clip the frame rendering.\n",
after: "({\n" +
" withBackground: true,\n" +
" theme: 'light',\n" +
" frameRendering: { enabled: true, name: true, outline: true, clip: true },\n" +
" padding: 10,\n" +
" selectedOnly: false,\n" +
" skipInliningFonts: false,\n" +
" embedScene: false,\n" +
"});",
},
{
field: "getPagePDFDimensions",
code: "getPagePDFDimensions(pageSize: PageSize, orientation: PageOrientation): PageDimensions",
Expand All @@ -273,12 +302,8 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
"\n" +
"@typedef {\"A0\" | \"A1\" | \"A2\" | \"A3\" | \"A4\" | \"A5\" | \"Letter\" | \"Legal\" | \"Tabloid\"} PageSize\n" +
"\n" +
"@typedef {\"portrait\" | \"landscape\"} PageOrientation\n" +
"\n" +
"@example\n" +
"const dimensions = getPDFPageDimensions(\"A4\", \"portrait\");\n" +
"console.log(dimensions); // { width: 595.28, height: 841.89 }",
after: "",
"@typedef {\"portrait\" | \"landscape\"} PageOrientation",
after: "(\"A4\", \"portrait\");",
},
{
field: "createPNG",
Expand Down
66 changes: 66 additions & 0 deletions src/shared/ExcalidrawAutomate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ import {
wrapTextAtCharLength,
arrayToMap,
addAppendUpdateCustomData,
getSVG,
getWithBackground,
} from "src/utils/utils";
import { getAttachmentsFolderAndFilePath, getExcalidrawViews, getLeaf, getNewOrAdjacentLeaf, isObsidianThemeDark, mergeMarkdownFiles, openLeaf } from "src/utils/obsidianUtils";
import { AppState, BinaryFileData, DataURL, ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/excalidraw/types";
Expand Down Expand Up @@ -84,6 +86,7 @@ import { GlobalPoint } from "@zsviczian/excalidraw/types/math/types";
import { AddImageOptions, ImageInfo, SVGColorInfo } from "src/types/excalidrawAutomateTypes";
import { _measureText, cloneElement, createPNG, createSVG, errorMessage, filterColorMap, getEmbeddedFileForImageElment, getFontFamily, getLineBox, getTemplate, isColorStringTransparent, isSVGColorInfo, mergeColorMapIntoSVGColorInfo, normalizeLinePoints, repositionElementsToCursor, svgColorInfoToColorMap, updateOrAddSVGColorInfo, verifyMinimumPluginVersion } from "src/utils/excalidrawAutomateUtils";
import { exportToPDF, getMarginValue, getPageDimensions, PageDimensions, PageOrientation, PageSize, PDFExportScale, PDFPageProperties } from "src/utils/exportUtils";
import { FrameRenderingOptions } from "src/types/utilTypes";

extendPlugins([
HarmonyPlugin,
Expand Down Expand Up @@ -969,6 +972,7 @@ export class ExcalidrawAutomate {
* margin: { left: 20, right: 20, top: 20, bottom: 20 },
* alignment: "center",
* }
* filename: "example.pdf",
* });
*/
async createPDF({
Expand Down Expand Up @@ -1002,6 +1006,68 @@ export class ExcalidrawAutomate {
await exportToPDF({SVG, scale, pageProps, filename});
}

/**
* Creates an SVG representation of the current view.
*
* @param {Object} options - The options for creating the SVG.
* @param {boolean} [options.withBackground=true] - Whether to include the background in the SVG.
* @param {"light" | "dark"} [options.theme] - The theme to use for the SVG.
* @param {FrameRenderingOptions} [options.frameRendering={enabled: true, name: true, outline: true, clip: true}] - The frame rendering options.
* @param {number} [options.padding] - The padding to apply around the SVG.
* @param {boolean} [options.selectedOnly=false] - Whether to include only the selected elements in the SVG.
* @param {boolean} [options.skipInliningFonts=false] - Whether to skip inlining fonts in the SVG.
* @param {boolean} [options.embedScene=false] - Whether to embed the scene in the SVG.
* @returns {Promise<SVGSVGElement>} A promise that resolves to the SVG element.
*/
async createViewSVG({
withBackground = true,
theme,
frameRendering = {enabled: true, name: true, outline: true, clip: true},
padding,
selectedOnly = false,
skipInliningFonts = false,
embedScene = false,
} : {
withBackground?: boolean,
theme?: "light" | "dark",
frameRendering?: FrameRenderingOptions,
padding?: number,
selectedOnly?: boolean,
skipInliningFonts?: boolean,
embedScene?: boolean,
}): Promise<SVGSVGElement> {
if(!this.targetView || !this.targetView.file || !this.targetView._loaded) {
console.log("No view loaded");
return;
}
const view = this.targetView;
const scene = this.targetView.getScene(selectedOnly);

const exportSettings: ExportSettings = {
withBackground: view.getViewExportWithBackground(withBackground),
withTheme: true,
isMask: isMaskFile(this.plugin, view.file),
skipInliningFonts,
frameRendering,
};

return await getSVG(
{
...scene,
...{
appState: {
...scene.appState,
theme: view.getViewExportTheme(theme),
exportEmbedScene: view.getViewExportEmbedScene(embedScene),
},
},
},
exportSettings,
view.getViewExportPadding(padding),
view.file,
);
}

/**
* Creates an SVG image from the ExcalidrawAutomate elements and the template provided.
* @param {string} [templatePath] - The template path to use for the SVG.
Expand Down
7 changes: 7 additions & 0 deletions src/types/utilTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,11 @@ export enum PreviewImageType {
PNG = "PNG",
SVGIMG = "SVGIMG",
SVG = "SVG"
}

export interface FrameRenderingOptions {
enabled: boolean;
name: boolean;
outline: boolean;
clip: boolean;
}
15 changes: 10 additions & 5 deletions src/utils/exportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ async function printPdf(
}

function calculateDimensions(
svg: SVGSVGElement,
svgWidth: number,
svgHeight: number,
pageDim: PageDimensions,
Expand All @@ -219,6 +220,9 @@ function calculateDimensions(
}[],
pages: number
} {
const viewBox = svg.getAttribute('viewBox')?.split(' ').map(Number) || [0, 0, svgWidth, svgHeight];
const [viewBoxX, viewBoxY] = viewBox;

const availableWidth = pageDim.width - margin.left - margin.right;
const availableHeight = pageDim.height - margin.top - margin.bottom;

Expand Down Expand Up @@ -262,7 +266,7 @@ function calculateDimensions(

return {
tiles: [{
viewBox: `0 0 ${svgWidth} ${svgHeight}`,
viewBox: `${viewBoxX} ${viewBoxY} ${svgWidth} ${svgHeight}`,
width: finalWidth,
height: finalHeight,
x: position.x,
Expand Down Expand Up @@ -297,7 +301,7 @@ function calculateDimensions(
);

tiles.push({
viewBox: `${tileX} ${tileY} ${tileWidth} ${tileHeight}`,
viewBox: `${tileX + viewBoxX} ${tileY + viewBoxY} ${tileWidth} ${tileHeight}`,
width: scaledTileWidth,
height: scaledTileHeight,
x: position.x,
Expand Down Expand Up @@ -364,12 +368,13 @@ export async function exportToPDF({
allPagesDiv.style.width = "100%";
allPagesDiv.style.height = "fit-content";

let j = 1;
let j = 0;
for (const svg of SVG) {
const svgWidth = parseFloat(svg.getAttribute('width') || '0');
const svgHeight = parseFloat(svg.getAttribute('height') || '0');

const {tiles} = calculateDimensions(
svg,
svgWidth,
svgHeight,
pageProps.dimensions,
Expand All @@ -378,7 +383,7 @@ export async function exportToPDF({
pageProps.alignment
);

let i = 1;
let i = 0;
for (const tile of tiles) {
const pageDiv = createDiv();
pageDiv.style.width = `${width}px`;
Expand All @@ -394,7 +399,7 @@ export async function exportToPDF({
clonedSVG.style.height = `${tile.height}px`;
clonedSVG.style.position = 'absolute';
clonedSVG.style.left = `${tile.x}px`;
clonedSVG.style.top = `${tile.y + (i-1)*height}px`;
clonedSVG.style.top = `${tile.y + (i+j)*height}px`;

pageDiv.appendChild(clonedSVG);
allPagesDiv.appendChild(pageDiv);
Expand Down
4 changes: 2 additions & 2 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ export async function getSVG (
: {},
},
files: scene.files,
exportPadding: exportSettings.frameRendering ? 0 : padding,
exportPadding: exportSettings.frameRendering?.enabled ? 0 : padding,
exportingFrame: null,
renderEmbeddables: true,
skipInliningFonts: exportSettings.skipInliningFonts,
Expand Down Expand Up @@ -365,7 +365,7 @@ export async function getPNG (
: {},
},
files: filterFiles(scene.files),
exportPadding: exportSettings.frameRendering ? 0 : padding,
exportPadding: exportSettings.frameRendering.enabled ? 0 : padding,
mimeType: "image/png",
getDimensions: (width: number, height: number) => ({
width: width * scale,
Expand Down
Loading

0 comments on commit b0bc034

Please sign in to comment.