Skip to content

feat: support embed fonts #1302

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions demos/browser/js/pptxgen.bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion demos/browser/js/pptxgen.bundle.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dist/pptxgen.bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/pptxgen.bundle.js.map

Large diffs are not rendered by default.

105 changes: 84 additions & 21 deletions dist/pptxgen.cjs.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* PptxGenJS 3.13.0-beta.0 @ 2023-05-17T03:15:58.384Z */
/* PptxGenJS 3.13.0-beta.1 @ 2023-11-23T12:40:58.506Z */
'use strict';

var JSZip = require('jszip');
Expand Down Expand Up @@ -807,6 +807,15 @@ function createColorElement(colorStr, innerElements) {
var colorAttr = 'val="' + (REGEX_HEX_COLOR.test(colorVal) ? colorVal.toUpperCase() : colorVal) + '"';
return innerElements ? "<a:".concat(tagName, " ").concat(colorAttr, ">").concat(innerElements, "</a:").concat(tagName, ">") : "<a:".concat(tagName, " ").concat(colorAttr, "/>");
}
function createTextShadow(options) {
var strXml = '';
var nOptions = __assign(__assign({}, options), { type: 'outer', offset: valToPts(options.offset || 4), angle: Math.round((options.angle || 270) * 60000), opacity: Math.round((options.opacity || 0.75) * 100000), color: options.color || DEF_TEXT_SHADOW.color });
strXml += " <a:outerShdw ".concat(nOptions.type === 'outer' ? 'sx="100000" sy="100000" kx="0" ky="0" algn="bl" rotWithShape="0"' : '', " blurRad=\"").concat(nOptions.blur, "\" dist=\"").concat(nOptions.offset, "\" dir=\"").concat(nOptions.angle, "\">");
strXml += " <a:srgbClr val=\"".concat(nOptions.color, "\">");
strXml += " <a:alpha val=\"".concat(nOptions.opacity, "\"/></a:srgbClr>");
strXml += ' </a:outerShdw>';
return strXml;
}
/**
* Creates `a:glow` element
* @param {TextGlowProps} options glow properties
Expand Down Expand Up @@ -5036,9 +5045,7 @@ function createSvgPngPreview(rel) {
});
}

/**
* PptxGenJS: XML Generation
*/
/* eslint-disable no-tabs */
var ImageSizingXml = {
cover: function (imgSize, boxDim) {
var imgRatio = imgSize.h / imgSize.w;
Expand Down Expand Up @@ -5947,8 +5954,16 @@ function genXmlTextRunProperties(opts, isDefault) {
runProps += "<a:highlight>".concat(createColorElement(opts.highlight), "</a:highlight>");
if (typeof opts.underline === 'object' && opts.underline.color)
runProps += "<a:uFill>".concat(genXmlColorSelection(opts.underline.color), "</a:uFill>");
if (opts.glow)
runProps += "<a:effectLst>".concat(createGlowElement(opts.glow, DEF_TEXT_GLOW), "</a:effectLst>");
if (opts.glow || opts.textShadow) {
runProps += '<a:effectLst>';
if (opts.glow) {
runProps += createGlowElement(opts.glow, DEF_TEXT_GLOW);
}
if (opts.textShadow) {
runProps += createTextShadow(opts.textShadow);
}
runProps += '</a:effectLst>';
}
if (opts.fontFace) {
// NOTE: 'cs' = Complex Script, 'ea' = East Asian (use "-120" instead of "0" - per Issue #174); ea must come first (Issue #174)
runProps += "<a:latin typeface=\"".concat(opts.fontFace, "\" pitchFamily=\"34\" charset=\"0\"/><a:ea typeface=\"").concat(opts.fontFace, "\" pitchFamily=\"34\" charset=\"-122\"/><a:cs typeface=\"").concat(opts.fontFace, "\" pitchFamily=\"34\" charset=\"-120\"/>");
Expand Down Expand Up @@ -6304,9 +6319,13 @@ function genXmlPlaceholder(placeholderObj) {
* @param {PresSlide} masterSlide - master slide
* @returns XML
*/
function makeXmlContTypes(slides, slideLayouts, masterSlide) {
function makeXmlContTypes(slides, slideLayouts, masterSlide, hasEmbedFonts) {
var strXml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + CRLF;
strXml += '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">';
// for fntdata
if (hasEmbedFonts) {
strXml += '<Default Extension="fntdata" ContentType="application/x-fontdata"/>';
}
strXml += '<Default Extension="xml" ContentType="application/xml"/>';
strXml += '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>';
strXml += '<Default Extension="jpeg" ContentType="image/jpeg"/>';
Expand Down Expand Up @@ -6400,7 +6419,7 @@ function makeXmlCore(title, subject, author, revision) {
* @param {PresSlide[]} slides - Presenation Slides
* @returns XML
*/
function makeXmlPresentationRels(slides) {
function makeXmlPresentationRels(slides, fonts, updateFonts) {
var intRelNum = 1;
var strXml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + CRLF;
strXml += '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">';
Expand All @@ -6414,8 +6433,17 @@ function makeXmlPresentationRels(slides) {
"<Relationship Id=\"rId".concat(intRelNum + 1, "\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/presProps\" Target=\"presProps.xml\"/>") +
"<Relationship Id=\"rId".concat(intRelNum + 2, "\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/viewProps\" Target=\"viewProps.xml\"/>") +
"<Relationship Id=\"rId".concat(intRelNum + 3, "\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme\" Target=\"theme/theme1.xml\"/>") +
"<Relationship Id=\"rId".concat(intRelNum + 4, "\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/tableStyles\" Target=\"tableStyles.xml\"/>") +
'</Relationships>';
"<Relationship Id=\"rId".concat(intRelNum + 4, "\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/tableStyles\" Target=\"tableStyles.xml\"/>");
var nFonts = fonts.slice();
// add font rels
fonts.forEach(function (_a, fontIdx) {
var fntDataPath = _a.fntDataPath;
var rId = "rId".concat(intRelNum + 5 + fontIdx);
nFonts[fontIdx].rId = rId;
strXml += "<Relationship Id=\"".concat(rId, "\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/font\" Target=\"").concat(fntDataPath, "\"/>");
});
updateFonts(nFonts);
strXml += '</Relationships>';
return strXml;
}
// XML-GEN: Functions that run 1-N times (once for each Slide)
Expand Down Expand Up @@ -6615,8 +6643,9 @@ function makeXmlTheme(pres) {
* @return {string} XML
*/
function makeXmlPresentation(pres) {
var hasEmbedFonts = pres.fonts.length > 0;
var strXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>".concat(CRLF) +
'<p:presentation xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" ' +
"<p:presentation ".concat(hasEmbedFonts ? 'embedTrueTypeFonts="1" ' : '', "xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" ") +
"xmlns:p=\"http://schemas.openxmlformats.org/presentationml/2006/main\" ".concat(pres.rtlMode ? 'rtl="1"' : '', " saveSubsetFonts=\"1\" autoCompressPictures=\"0\">");
// STEP 1: Add slide master (SPEC: tag 1 under <presentation>)
strXml += '<p:sldMasterIdLst><p:sldMasterId id="2147483648" r:id="rId1"/></p:sldMasterIdLst>';
Expand All @@ -6634,14 +6663,14 @@ function makeXmlPresentation(pres) {
strXml += "<p:sldSz cx=\"".concat(pres.presLayout.width, "\" cy=\"").concat(pres.presLayout.height, "\"/>");
strXml += "<p:notesSz cx=\"".concat(pres.presLayout.height, "\" cy=\"").concat(pres.presLayout.width, "\"/>");
// STEP 5: Add text styles
strXml += '<p:defaultTextStyle>';
for (var idy = 1; idy < 10; idy++) {
strXml +=
"<a:lvl".concat(idy, "pPr marL=\"").concat((idy - 1) * 457200, "\" algn=\"l\" defTabSz=\"914400\" rtl=\"0\" eaLnBrk=\"1\" latinLnBrk=\"0\" hangingPunct=\"1\">") +
'<a:defRPr sz="1800" kern="1200"><a:solidFill><a:schemeClr val="tx1"/></a:solidFill><a:latin typeface="+mn-lt"/><a:ea typeface="+mn-ea"/><a:cs typeface="+mn-cs"/>' +
"</a:defRPr></a:lvl".concat(idy, "pPr>");
}
strXml += '</p:defaultTextStyle>';
// strXml += '<p:defaultTextStyle>'
// for (let idy = 1; idy < 10; idy++) {
// strXml +=
// `<a:lvl${idy}pPr marL="${(idy - 1) * 457200}" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">` +
// '<a:defRPr sz="1800" kern="1200"><a:solidFill><a:schemeClr val="tx1"/></a:solidFill><a:latin typeface="+mn-lt"/><a:ea typeface="+mn-ea"/><a:cs typeface="+mn-cs"/>' +
// `</a:defRPr></a:lvl${idy}pPr>`
// }
// strXml += '</p:defaultTextStyle>'
// STEP 6: Add Sections (if any)
if (pres.sections && pres.sections.length > 0) {
strXml += '<p:extLst><p:ext uri="{521415D9-36F7-43E2-AB2F-B90AF26B5E84}">';
Expand All @@ -6655,6 +6684,17 @@ function makeXmlPresentation(pres) {
strXml += '<p:ext uri="{EFAFB233-063F-42B5-8137-9DF3F51BA10A}"><p15:sldGuideLst xmlns:p15="http://schemas.microsoft.com/office/powerpoint/2012/main"/></p:ext>';
strXml += '</p:extLst>';
}
if (hasEmbedFonts) {
strXml += '<p:embeddedFontLst>';
pres.fonts.forEach(function (_a) {
var rId = _a.rId, typeface = _a.typeface;
// fontdata
if (rId) {
strXml += "<p:embeddedFont><p:font typeface=\"".concat(typeface, "\" /><p:regular r:id=\"").concat(rId, "\"/></p:embeddedFont>");
}
});
strXml += '</p:embeddedFontLst>';
}
// Done
strXml += '</p:presentation>';
return strXml;
Expand Down Expand Up @@ -6740,6 +6780,7 @@ var PptxGenJS = /** @class */ (function () {
* @depricated use `ShapeType`
*/
this._shapes = SHAPE_TYPE;
this._fonts = [];
/**
* Provides an API for `addTableDefinition` to create slides as needed for auto-paging
* @param {AddSlideProps} options - slide masterName and/or sectionTitle
Expand Down Expand Up @@ -6862,6 +6903,11 @@ var PptxGenJS = /** @class */ (function () {
zip.folder('_rels');
zip.folder('docProps');
zip.folder('ppt').folder('_rels');
// add embed fonts
this._fonts.forEach(function (font, idx) {
font.fntDataPath = "fonts/font".concat(idx + 1, ".fntdata");
zip.file("ppt/".concat(font.fntDataPath), font.fontBlob);
});
zip.folder('ppt/charts').folder('_rels');
zip.folder('ppt/embeddings');
zip.folder('ppt/media');
Expand All @@ -6871,11 +6917,13 @@ var PptxGenJS = /** @class */ (function () {
zip.folder('ppt/theme');
zip.folder('ppt/notesMasters').folder('_rels');
zip.folder('ppt/notesSlides').folder('_rels');
zip.file('[Content_Types].xml', makeXmlContTypes(this.slides, this.slideLayouts, this.masterSlide)); // TODO: pass only `this` like below! 20200206
zip.file('[Content_Types].xml', makeXmlContTypes(this.slides, this.slideLayouts, this.masterSlide, this.fonts.length > 0)); // TODO: pass only `this` like below! 20200206
zip.file('_rels/.rels', makeXmlRootRels());
zip.file('docProps/app.xml', makeXmlApp(this.slides, this.company)); // TODO: pass only `this` like below! 20200206
zip.file('docProps/core.xml', makeXmlCore(this.title, this.subject, this.author, this.revision)); // TODO: pass only `this` like below! 20200206
zip.file('ppt/_rels/presentation.xml.rels', makeXmlPresentationRels(this.slides));
zip.file('ppt/_rels/presentation.xml.rels', makeXmlPresentationRels(this.slides, this.fonts, function (nFonts) {
_this._fonts = nFonts;
}));
zip.file('ppt/theme/theme1.xml', makeXmlTheme(this));
zip.file('ppt/presentation.xml', makeXmlPresentation(this));
zip.file('ppt/presProps.xml', makeXmlPresProps());
Expand Down Expand Up @@ -7196,6 +7244,13 @@ var PptxGenJS = /** @class */ (function () {
enumerable: false,
configurable: true
});
Object.defineProperty(PptxGenJS.prototype, "fonts", {
get: function () {
return this._fonts;
},
enumerable: false,
configurable: true
});
// EXPORT METHODS
/**
* Export the current Presentation to stream
Expand Down Expand Up @@ -7440,6 +7495,14 @@ var PptxGenJS = /** @class */ (function () {
// @note `verbose` option is undocumented; used for verbose output of layout process
genTableToSlides(this, eleId, options, (options === null || options === void 0 ? void 0 : options.masterSlideName) ? this.slideLayouts.filter(function (layout) { return layout._name === options.masterSlideName; })[0] : null);
};
/**
* Add an embed font to Presention
* @param {AddFontProps} props - font properties
*/
PptxGenJS.prototype.addFont = function (option) {
console.log('[genjs] addFont', option);
this._fonts.push(option);
};
return PptxGenJS;
}());

Expand Down
Loading