// // officegen: All the code to generate PPTX/PPTS files. // // Please refer to README.md for this module's documentations. // // Copyright (c) 2013 Ziv Barber; // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // /** * Basicgen plugin to create pptx files (Microsoft PowerPoint). */ var baseobj = require('../core/index.js') var OfficeChart = require('./officechart.js') var msdoc = require('../msdoc/msofficegen.js') var shapes = require('./shapes.js') var pptxFields = require('./pptxfields.js') var officeTable = require('./genofficetable') var path = require('path') var fast_image_size = require('fast-image-size') var excelbuilder = require('./msexcel-builder.js') var xmlBuilder = require('xmlbuilder') var docplugman = require('../core/docplug') // Officegen pptx plugins: var plugWidescreen = require('./pptxplg-widescreen') var plugSpeakernotes = require('./pptxplg-speakernotes') var plugLayouts = require('./pptxplg-layouts') // BMK_PPTX_PLUG: function encodeHTML(inStr) { return inStr .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') } var GLOBAL_CHART_COUNT = 0 /** * Extend officegen object with PPTX/PPSX support. * * This method extending the given officegen object to create PPTX/PPSX document. * * @param {object} genobj The object to extend. * @param {string} new_type The type of object to create. * @param {object} options The object's options. * @param {object} gen_private Access to the internals of this object. * @param {object} type_info Additional information about this type. * @constructor * @name makePptx */ function makePptx(genobj, new_type, options, gen_private, type_info) { /** * Prepare the default data. * @param {object} docpluginman Access to the document plugins manager. */ function setDefaultDocValues(docpluginman) { var pptxData = docpluginman.getDataStorage() // Please put any setting that API can override here: pptxData.EMUS_PER_PT = 12700 pptxData.pptWidth = 720 * pptxData.EMUS_PER_PT pptxData.pptHeight = 540 * pptxData.EMUS_PER_PT pptxData.pptType = 'screen4x3' // Rels im the main rels file that depended on the data and must be added after the slides: pptxData.extraMainRelList = [] } // Allow you to control the view: genobj.view = { restoredLeft: 15620, restoredTop: 94660 } // Use this method to create the options for any add* method: genobj.createShapeOptions = shapes.createShapeOptions genobj.shapes = shapes.shapes genobj.fields = pptxFields genobj.options = options && typeof options === 'object' ? options : {} // Temporary, I'll create a new code without the need to create a temp file (Ziv Barber, 2016-06-23): if (!genobj.options.tempDir) { genobj.options.tempDir = './' } // Endif. /** * Prepare everything to generate PPTX files. * * This method checking for extra resources needed to add by the generator engine. */ function cbPreparePptxToGenerate() { genobj.generate_data = {} // Tell all the features (plugins) that we are about to generate a new document zip: gen_private.features.type.pptx.emitEvent('beforeGen', genobj) // Allow some plugins to do more stuff after all the plugins added their data: gen_private.features.type.pptx.emitEvent('beforeGenFinal', genobj) // Apple Keynote requires these added *after* all slides: gen_private.type.msoffice.rels_app.push( { type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/presProps', target: 'presProps.xml', clear: 'type' }, { type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/viewProps', target: 'viewProps.xml', clear: 'type' }, { type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme', target: 'theme/theme1.xml', clear: 'type' } ) // This event allow the plugins to add permanent rels that must be at the end: var extraMainRelList = [] gen_private.features.type.pptx.emitEvent('addMainRels', { genobj: genobj, relsList: extraMainRelList }) extraMainRelList.forEach(function(value) { gen_private.type.msoffice.rels_app.push(value) }) // Add any extra rels needed by the plugins and depended on the data: if ( gen_private.type.pptx.extraMainRelList && typeof gen_private.type.pptx.extraMainRelList === 'object' && gen_private.type.pptx.extraMainRelList.forEach ) { gen_private.type.pptx.extraMainRelList.forEach(function(value) { gen_private.type.msoffice.rels_app.push(value) }) } gen_private.type.msoffice.rels_app.push({ type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/tableStyles', target: 'tableStyles.xml', clear: 'type' }) } /** * Create the 'presProps.xml' resource. * * @param {object} data Ignored by this callback function. * @return Text string. */ function cbMakePptxPresProps(data) { return ( gen_private.plugs.type.msoffice.cbMakeMsOfficeBasicXml(data) + '' ) } /** * Create the 'tableStyles.xml' resource. * * @param {object} data Ignored by this callback function. * @return Text string. */ function cbMakePptxStyles(data) { return ( gen_private.plugs.type.msoffice.cbMakeMsOfficeBasicXml(data) + '' ) } /** * Create the 'viewProps.xml' resource. * * @param {object} data Ignored by this callback function. * @return Text string. */ function cbMakePptxViewProps(data) { var restoredLeft = data.view.restoredLeft || 15620 var restoredTop = data.view.restoredTop || 94660 return ( gen_private.plugs.type.msoffice.cbMakeMsOfficeBasicXml(data) + '' ) } /** * Create the 'slideLayout1.xml' resource. * * @param {object} data Ignored by this callback function. * @return Text string. */ function cbMakePptxLayout1(data) { if (!data || typeof data !== 'object') { data = {} } // Endif. // You can place here the title: var ph1 = 'Click to edit Master title style' if (data.ph1 && data.slide && data.ph1.length) { ph1 = createXmlSlideParagraph('', data.ph1, {}, data.slide) } // Endif. // The sub-title: var ph2 = 'Click to edit Master subtitle style' if (data.ph2 && data.slide && data.ph2.length) { ph2 = createXmlSlideParagraph('', data.ph2, {}, data.slide) } // Endif. var ph3 = createFieldText('DATE_TIME', 1, data.useDate) var footFull = '' var ft = '' var curElNum = 4 if (data.isDate || !data.isRealSlide) { footFull += '' + ph3 + '' curElNum++ } // Endif. if (data.isFooter && data.ft && data.slide && data.ft.length) { ft = createXmlSlideParagraph('', data.ft, {}, data.slide) footFull += '' + ft + '' curElNum++ } else if (!data.isRealSlide) { footFull += '' curElNum++ } // Endif. if (data.isSlideNum || !data.isRealSlide) { footFull += '‹#›' curElNum++ } // Endif. return ( gen_private.plugs.type.msoffice.cbMakeMsOfficeBasicXml(data) + '' + ph1 + '' + ph2 + '' + footFull + '' ) } /** * Create the main presentation resource. * * This resource is the main resource of any PowerPoint document. * * @param {object} data Ignored by this callback function. * @return Text string. */ function cbMakePptxPresentation(data) { var outString = gen_private.plugs.type.msoffice.cbMakeMsOfficeBasicXml(data) + '' // Signal to the plugins to add any extra xml needed for this file: var dataToPlugs = { data: outString } gen_private.features.type.pptx.emitEvent('presentationGen', dataToPlugs) outString = dataToPlugs.data + '' for ( var i = 0, total_size = gen_private.pages.length; i < total_size; i++ ) { outString += '' } // End of for loop. outString += '' var curPos = 0 for (i = 1; i < 10; i++) { outString += '' curPos += 457200 } // End of for loop. outString += '' outString += '' return outString } /** * Create the slides masters resource. * * @param {object} data Ignored by this callback function. * @return Text string. */ function cbMakePptxSlideMasters(data) { var outData = gen_private.plugs.type.msoffice.cbMakeMsOfficeBasicXml(data) + '' // The master slide itself: if (gen_private.masterSlideXmlCode) { outData += '' + gen_private.masterSlideXmlCode + '' } else { outData += 'Click to edit Master title styleClick to edit Master text stylesSecond levelThird levelFourth levelFifth level6/13/2013�#�' } // Endif. outData += '' var curRelId = 1 var curId = 2147483649 var getDocData = plugsmanObj.getDataStorage() // Add all the slide layouts needed: outData += '' curRelId++ curId++ if ( getDocData.slideLayouts && typeof getDocData.slideLayouts === 'object' ) { for (var item in getDocData.slideLayouts) { if (getDocData.slideLayouts[item]) { outData += '' curRelId++ curId++ } // Endif. } // End of for loop. } // Endif. outData += '' return outData } /** * Translate field_name into the text real value. * * This method creating the text to display for the given field. * * @param {string} field_name the name of the field. * @param {number} slide_num current slide number. * @param {Date} useDate Optional date to use instead of the current date. * @return The text string data. */ function createFieldText(field_name, slide_num, useDate) { var curDateTime = useDate ? new Date(useDate) : new Date() var dayInWeek = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ] var monthsList = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ] var monthsShortList = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] var outValue = '' // curDateTime.getDate () Returns the day of the month (from 1-31) // curDateTime.getDay () Returns the day of the week (from 0-6) // curDateTime.getFullYear () Returns the year (four digits) // curDateTime.getHours () Returns the hour (from 0-23) // curDateTime.getMinutes () Returns the minutes (from 0-59) // curDateTime.getMonth () Returns the month (from 0-11) // curDateTime.getSeconds () Returns the seconds (from 0-59) switch (field_name) { // presentation slide number: case 'SLIDE_NUM': case 'slidenum': outValue += slide_num break // default date time format for the rendering application: case 'DATE_TIME': case 'datetime': outValue += curDateTime.getMonth() + 1 + '/' + curDateTime.getDate() + '/' + curDateTime.getFullYear() break // MM/DD/YYYY date time format (Example: 10/12/2007): case 'DATE_MM_DD_YYYY': case 'datetime1': outValue += curDateTime.getMonth() + 1 + '/' + curDateTime.getDate() + '/' + curDateTime.getFullYear() break // Day, Month DD, YYYY date time format (Example: Friday, October 12, 2007): case 'DATE_WD_MN_DD_YYYY': case 'datetime2': outValue += dayInWeek[curDateTime.getDay()] + ', ' + monthsList[curDateTime.getMonth()] + ' ' + curDateTime.getDate() + ', ' + curDateTime.getFullYear() break // DD Month YYYY date time format (Example: 12 October 2007): case 'DATE_DD_MN_YYYY': case 'datetime3': outValue += curDateTime.getDate() + ' ' + monthsList[curDateTime.getMonth()] + ' ' + curDateTime.getFullYear() break // Month DD, YYYY date time format (Example: October 12, 2007): case 'DATE_MN_DD_YYYY': case 'datetime4': outValue += monthsList[curDateTime.getMonth()] + ' ' + curDateTime.getDate() + ', ' + curDateTime.getFullYear() break // DD-Mon-YY date time format (Example: 12-Oct-07): case 'DATE_DD_SMN_YY': case 'datetime5': outValue += curDateTime.getDate() + '-' + monthsShortList[curDateTime.getMonth()] + '-' + (curDateTime.getFullYear() % 100) break // Month YY date time format (Example: October 07): case 'DATE_MM_YY': case 'datetime6': outValue += monthsList[curDateTime.getMonth()] + ' ' + (curDateTime.getFullYear() % 100) break // Mon-YY date time format (Example: Oct-07): case 'DATE_SMN_YY': case 'datetime7': outValue += monthsShortList[curDateTime.getMonth()] + '-' + (curDateTime.getFullYear() % 100) break // MM/DD/YYYY hh:mm AM/PM date time format (Example: 10/12/2007 4:28 PM): case 'DATE_TIME_DD_MM_YYYY_HH_MM_PM': case 'datetime8': outValue += curDateTime.getMonth() + '/' + curDateTime.getDate() + '/' + curDateTime.getFullYear() outValue += (curDateTime.getHours() % 12) + ':' + curDateTime.getMinutes() outValue += curDateTime.getHours() > 11 ? ' PM' : ' AM' break // MM/DD/YYYY hh:mm:ss AM/PM date time format (Example: 10/12/2007 4:28:34 PM): case 'DATE_TIME_DD_MM_YYYY_HH_MM_SC_PM': case 'datetime9': outValue += curDateTime.getMonth() + '/' + curDateTime.getDate() + '/' + curDateTime.getFullYear() outValue += (curDateTime.getHours() % 12) + ':' + curDateTime.getMinutes() + ':' + curDateTime.getSeconds() outValue += curDateTime.getHours() > 11 ? ' PM' : ' AM' break // hh:mm date time format (Example: 16:28): case 'TIME_HH_MM': case 'datetime10': outValue += curDateTime.getHours() + ':' + curDateTime.getMinutes() break // hh:mm:ss date time format (Example: 16:28:34): case 'TIME_HH_MM_SC': case 'datetime11': outValue += curDateTime.getHours() + ':' + curDateTime.getMinutes() + ':' + curDateTime.getSeconds() break // hh:mm AM/PM date time format (Example: 4:28 PM): case 'TIME_HH_MM_PM': case 'datetime12': outValue += (curDateTime.getHours() % 12) + ':' + curDateTime.getMinutes() outValue += curDateTime.getHours() > 11 ? ' PM' : ' AM' break // hh:mm:ss: AM/PM date time format (Example: 4:28:34 PM): case 'TIME_HH_MM_SC_PM': case 'datetime13': outValue += (curDateTime.getHours() % 12) + ':' + curDateTime.getMinutes() + ':' + curDateTime.getSeconds() outValue += curDateTime.getHours() > 11 ? ' PM' : ' AM' break default: return null } // End of switch. return outValue } /** * ???. * * @param {object} text_info Information how to display the text. * @param {object} slide_obj The object of this slider. * @return Text string. */ function createXmlSlideTextData(text_info, slide_obj) { var out_obj = {} out_obj.font_size = '' out_obj.bold = '' out_obj.italic = '' out_obj.strike = '' out_obj.underline = '' out_obj.rpr_info = '' out_obj.char_spacing = '' out_obj.baseline = '' if (typeof text_info === 'object') { if (text_info.bold) { out_obj.bold = ' b="1"' } // Endif. if (text_info.italic) { out_obj.italic = ' i="1"' } // Endif. if (text_info.strike) { out_obj.strike = ' strike="sngStrike"' } // Endif. if (text_info.underline) { out_obj.underline = ' u="sng"' } // Endif. if (text_info.font_size) { out_obj.font_size = ' sz="' + text_info.font_size + '00"' } // Endif. // psv 2015-01-21 Manually copied in from https://github.com/Ziv-Barber/officegen/pull/41/files if (text_info.char_spacing) { out_obj.char_spacing = ' spc="' + text_info.char_spacing * 100 + '"' // Must also disable kerning otherwise text won't actually expand: out_obj.char_spacing += ' kern="0"' } // Endif. if (text_info.baseline) { out_obj.baseline = ' baseline="' + text_info.baseline * 1000 + '"' } // Endif. if (text_info.color) { out_obj.rpr_info += shapes.createColorElements(text_info.color) } else if (slide_obj && slide_obj.color) { out_obj.rpr_info += shapes.createColorElements(slide_obj.color) } // Endif. if (text_info.font_face) { out_obj.rpr_info += '' } // Endif. } else { if (slide_obj && slide_obj.color) { out_obj.rpr_info += shapes.createColorElements(slide_obj.color) } // Endif. } // Endif. if (out_obj.rpr_info !== '') { out_obj.rpr_info += '' } // Endif. return out_obj } /** * Create a text object for adding into a slide. * * @param {object} text_info Information how to display the text. * @param {object} text_string The text string or requested field. * @param {object} slide_obj The object of this slider. * @param {object} slide_num Current slide number. * @param {string} out_styles Paragraph style used to style paragraphs generated by newlines * @return The PPTX code. */ function createXmlSlideTextObject( text_info, text_string, slide_obj, slide_num, out_styles ) { text_info = text_info || {} var area_opt_data = createXmlSlideTextData(text_info, slide_obj) var textStyles = [ 'font_size', 'strike', 'italic', 'bold', 'underline', 'char_spacing', 'baseline' ].reduce(function(acc, attr) { return acc + area_opt_data[attr] }, '') var parsedText var startInfo = '' + area_opt_data.rpr_info : '/>') + '' var endTag = '' var outData = '' + startInfo if (text_string.field) { endTag = '' var outTextField = pptxFields[text_string.field] if (outTextField === null) { for (var fieldIntName in pptxFields) { if (pptxFields[fieldIntName] === text_string.field) { outTextField = text_string.field break } // Endif. } // End of for loop. if (outTextField === null) { outTextField = 'datetime' } // Endif. } // Endif. outData = '' + startInfo outData += createFieldText(outTextField, slide_num) } else { // Automatic support for newline - split it into multi-p: parsedText = text_string.split('\n') if (parsedText.length > 1) { var outTextData = '' for ( var i = 0, total_size_i = parsedText.length; i < total_size_i; i++ ) { outTextData += outData + encodeHTML(parsedText[i]) if (i + 1 < total_size_i) { outTextData += '' if (out_styles) outTextData += out_styles } // Endif. } // End of for loop. outData = outTextData } else { outData += encodeHTML(text_string) } // Endif. } // Endif. var outBreakP = '' if (text_info.breakLine) { outBreakP += '' } // Endif. return outData + '' + endTag + outBreakP } /** * Create all the objects inside a single paragraph. * @param {string} outString The string to add the output xml to it. * @param {Array} pData Array with all the parts of this paragraph. * @param {object} pOptions Paragraph options. * @param {object} slideObj The object of this slider. */ function createXmlSlideParagraph(outString, pData, pOptions, slideObj) { var outStyles = '' var moreStylesAttr var moreStyles // Work on all the parts of this paragraph: for (var j = 0, total_size_j = pData.length; j < total_size_j; j++) { if (pData[j]) { moreStylesAttr = '' moreStyles = '' if (pData[j].options) { if (pData[j].options.align) { switch (pData[j].options.align) { case 'right': moreStylesAttr += ' algn="r"' break case 'center': moreStylesAttr += ' algn="ctr"' break case 'justify': moreStylesAttr += ' algn="just"' break } // End of switch. } // Endif. if (pData[j].options.indentLevel > 0) { moreStylesAttr += ' lvl="' + pData[j].options.indentLevel + '"' } // Endif. if (pData[j].options.listType === 'number') { moreStyles += '' } else if (pData[j].options.listType === 'dot') { moreStyles += '' } else { if (pData[j].options.listType) { moreStyles += '' } // Endif. if (pData[j].options.listTypeChar) { moreStyles += '' } // Endif. } // Endif. } // Endif. if (moreStyles !== '') { outStyles = '' + moreStyles + '' } else if (moreStylesAttr !== '') { outStyles = '' } // Endif. if (outStyles || !j) { if (j) { outString += '' } // Endif. outString += '' + outStyles } // Endif. outString += createXmlSlideTextObject( pData[j].options, pData[j].text, slideObj, slideObj.getPageNumber(), outStyles ) } // Endif. } // End of for loop - adding all the objects inside the paragraph. var font_size = '' if (pOptions && pOptions.font_size) { font_size = ' sz="' + pOptions.font_size + '00"' } // Endif. outString += '' return outString } /** * ???. * * @param {object} in_data_val Input value as passed by the user. * @param {number} max_value Maximum value allowed. * @param {number} def_value Default value. * @param {number} auto_val ???. * @param {number} mul_val If you didn't provide a unit then we'll multiplay the given value with this value. * @return ???. */ function parseSmartNumber( in_data_val, max_value, def_value, auto_val, mul_val ) { if (typeof in_data_val === 'undefined') { return typeof def_value === 'number' ? def_value : 0 } // Endif. if (in_data_val === '') { in_data_val = 0 } // Endif. var unitPos if (typeof in_data_val === 'string') { unitPos = in_data_val.search(/(inch|in)$/i) if (unitPos >= 0) { return parseInt(in_data_val.slice(0, unitPos), 10) * 914400 } // Endif. unitPos = in_data_val.search(/cm$/i) if (unitPos >= 0) { return parseInt(in_data_val.slice(0, unitPos), 10) * 360000 } // Endif. unitPos = in_data_val.search(/mm$/i) if (unitPos >= 0) { return parseInt(in_data_val.slice(0, unitPos), 10) * 36000 } // Endif. unitPos = in_data_val.search(/emu{0,1}$/i) if (unitPos >= 0) { return parseInt(in_data_val.slice(0, unitPos), 10) } // Endif. unitPos = in_data_val.search(/(point|pt)$/i) if (unitPos >= 0) { return parseInt(in_data_val.slice(0, unitPos), 10) * 12700 } // Endif. unitPos = in_data_val.search(/(pica|pc)$/i) if (unitPos >= 0) { return parseInt(in_data_val.slice(0, unitPos), 10) * 12700 * 12 } // Endif. } // Endif. if (typeof in_data_val === 'string' && !isNaN(in_data_val)) { in_data_val = parseInt(in_data_val, 10) } // Endif. var realNum = Math.round(mul_val ? in_data_val * mul_val : in_data_val) var realVal var realMax if (typeof in_data_val === 'string') { if (in_data_val.indexOf('%') !== -1) { realMax = typeof max_value === 'number' ? max_value : 0 if (realMax <= 0) { return 0 } // Endif. realVal = parseInt(in_data_val, 10) return Math.round((realMax / 100) * realVal) } // Endif. if (in_data_val.indexOf('#') !== -1) { realVal = parseInt(in_data_val, 10) return realMax } // Endif. var realAuto = typeof auto_val === 'number' ? auto_val : 0 if (in_data_val === '*') { return realAuto } // Endif. if (in_data_val === 'c') { return Math.round(realAuto / 2) } // Endif. } // Endif. if (typeof in_data_val === 'number') { return realNum } // Endif. return typeof def_value === 'number' ? def_value : 0 } /** * Create the XML code of a single effect. * * This method creating the effect XML code for a single object. * * @param {object} effectData Effect data. * @param {string} effectName The name of the effect. */ function generateEffects(effectData, effectName) { var outData = ' 100 || alphaPer < 0) { alphaPer = 60 } alphaPer = (100 - alphaPer) * 1000 if (effectData.align) { if (effectData.align.top) { algnData += 't' } if (effectData.align.bottom) { algnData += 'b' } if (effectData.align.left) { algnData += 'l' } if (effectData.align.right) { algnData += 'r' } } // Endif. if (algnData === '') { algnData = 'br' } // Endif. // Size // Blur // Angle // Distance // BMK_TODO: outData += ' blurRad="' + blurRad + '" dist="' + dist + '" dir="' + dir + '" algn="' + algnData + '" rotWithShape="0"' // sx="24000" sy="24000" // BMK_TODO: outData += '>' return outData + '' } /** * Create the body properties code for text. * * This method creating the XML code of the body properties of a text. * * @return The body properties XML code. */ function createBodyProperties(objOptions) { var bodyProperties = '' } else if (objOptions.bodyProp.normAutofit) { bodyProperties += '' } else if (objOptions.bodyProp.autoFit !== false) { bodyProperties += '' } // Endif. bodyProperties += '' // Default: } else { bodyProperties += ' wrap="square" rtlCol="0">' } // Endif. return bodyProperties } /** * Generate a slider resource. * * This function generating a slider XML resource. * * @param {object} data The main slide object. * @param {boolean} makeOnlyObjects is true to turn this method to make only the objects in data.data. * @return Text string. */ function cbMakePptxSlide(data, makeOnlyObjects) { var outString = '' var objs_list = data.data var timingData = '' // Slide with custom xml code generating (we are using it for slides that using the bulit-in layouts): if ( data.slide.useLayout && typeof data.slide.useLayout === 'object' && data.slide.useLayout.mkResCb ) { // So in this case we are bypassing the normal slide generating and using a custom generating: data.slide.useLayout.slide = data.slide // To use a custom slide generator just declare data.slide.useLayout.mkResCb to be a cb that receive data.slide.useLayout itself. return data.slide.useLayout.mkResCb(data.slide.useLayout) } // Endif. // Allow you to turn this method into layout generator: var slideElement = data.slide.layoutName ? 'Layout' : '' var slideElementA = '' if (data.slide.layoutName) { if (data.slide.officeType) { slideElementA += ' type="' + data.slide.layoutName + '"' } // Endif. slideElementA += ' preserve="1"' if (!data.slide.officeType) { slideElementA += ' userDrawn="1"' } // Endif. } // Endif. // Create the header of the slide (only if we need that): if (!makeOnlyObjects) { outString = gen_private.plugs.type.msoffice.cbMakeMsOfficeBasicXml(data) + '' if (data.slide.back) { outString += shapes.createColorElements(false, data.slide.back) } // Endif. outString += '' } // Endif. // Loop on all the objects inside the slide to add it into the slide: for (var i = 0, total_size = objs_list.length; i < total_size; i++) { var x = 0 var y = 0 var cx = 2819400 var cy = 369332 var xDef = true var yDef = true var cyDef = true var cxDef = true var moreStyles = '' var moreStylesAttr = '' var outStyles = '' var styleData = '' var shapeType = null var locationAttr = '' if (objs_list[i].options) { if (typeof objs_list[i].options.cx !== 'undefined') { cxDef = false if (objs_list[i].options.cx) { cx = parseSmartNumber( objs_list[i].options.cx, gen_private.type.pptx.pptWidth, cx, gen_private.type.pptx.pptWidth, 10000 ) } else { cx = 1 } // Endif. } // Endif. if (typeof objs_list[i].options.cy !== 'undefined') { cyDef = false if (objs_list[i].options.cy) { cy = parseSmartNumber( objs_list[i].options.cy, gen_private.type.pptx.pptHeight, cy, gen_private.type.pptx.pptHeight, 10000 ) } else { cy = 1 } // Endif. } // Endif. if (objs_list[i].options.x) { xDef = false x = parseSmartNumber( objs_list[i].options.x, gen_private.type.pptx.pptWidth, 0, gen_private.type.pptx.pptWidth - cx, 10000 ) } // Endif. if (objs_list[i].options.y) { yDef = false y = parseSmartNumber( objs_list[i].options.y, gen_private.type.pptx.pptHeight, 0, gen_private.type.pptx.pptHeight - cy, 10000 ) } // Endif. if (objs_list[i].options.shape) { shapeType = shapes.getShapeInfo(objs_list[i].options.shape) } // Endif. if (objs_list[i].options.flip_vertical) { locationAttr += ' flipV="1"' } // Endif. if (objs_list[i].options.flip_horizontal) { locationAttr += ' flipH="1"' } // Endif. if (objs_list[i].options.rotate) { var rotateVal = objs_list[i].options.rotate > 360 ? objs_list[i].options.rotate - 360 : objs_list[i].options.rotate rotateVal *= 60000 locationAttr += ' rot="' + rotateVal + '"' } // Endif. } // Endif. // Check what type of object to add: switch (objs_list[i].type) { // TODO: remove hard code here: case 'table': var table_obj = officeTable.getTable( objs_list[i].data, objs_list[i].options ) // console.log(JSON.stringify(table_obj, null, 2)) var table_xml = xmlBuilder .create(table_obj, { version: '1.0', encoding: 'UTF-8', standalone: true }) .toString({ pretty: true, indent: ' ', newline: '\n' }) outString += table_xml break case 'chart': // loop through the charts // if (objs_list[i].renderType === 'pie') { outString += '' } else if (objs_list[i].renderType === 'column') { outString += '' } // Endif. break case 'text': case 'cxn': var effectsList = '' if (shapeType == null) shapeType = shapes.getShapeInfo(null) // if ( objs_list[i].type == 'text' ) { // if ( !objs_list[i].options || (!objs_list[i].options.cx && !objs_list[i].options.cx) ) { // objs_list[i].options = objs_list[i].options ? objs_list[i].options : {} // objs_list[i].options.bodyProp = objs_list[i].options.bodyProp ? objs_list[i].options.bodyProp : {} // objs_list[i].options.bodyProp.autoFit = true // cx = gen_private.type.pptx.pptWidth - x // cy = gen_private.type.pptx.pptHeight - y // } // Endif. // } // Endif. var pNvPr = '' if (objs_list[i].options.ph) { var extraPhAttr = '' if (objs_list[i].options.phSz) { extraPhAttr += ' sz="' + objs_list[i].options.phSz + '"' } // Endif. if (objs_list[i].options.phIdx) { extraPhAttr += ' idx="' + objs_list[i].options.phIdx + '"' } // Endif. pNvPr = '' + (objs_list[i].options.nvPrCode || '') + '' } // Endif. var codeCNvPrAttr = '' if (objs_list[i].options.id) { codeCNvPrAttr += ' id="' + objs_list[i].options.id + '"' } else { codeCNvPrAttr += ' id="' + (i + 2) + '"' } // Endif. if (objs_list[i].options.name) { codeCNvPrAttr += ' name="' + objs_list[i].options.name + '"' } else { codeCNvPrAttr += ' name="Object ' + (i + 2) + '"' } // Endif. if (objs_list[i].options.title) { codeCNvPrAttr += ' title="' + objs_list[i].options.title + '"' } // Endif. if (objs_list[i].options.desc) { codeCNvPrAttr += ' descr="' + objs_list[i].options.desc + '"' } // Endif. if (objs_list[i].options.hidden) { codeCNvPrAttr += ' hidden="1"' } // Endif. if (objs_list[i].type === 'cxn') { outString += '' outString += '' + pNvPr + '' } else { outString += '' outString += '' + pNvPr + '' } // Endif. var bwMode = objs_list[i].options.bwMode ? ' bwMode="' + objs_list[i].options.bwMode + '"' : '' if ( objs_list[i].options.ph && xDef && yDef && cxDef && cyDef && !locationAttr ) { outString += '' } else { outString += '' outString += '' if (!objs_list[i].options.ph || !yDef || !xDef) { outString += '' // string changed to take into account change of shape that you do by moving the little yellow dot if (shapeType.avLst !== {}) { outString += '' for (var adj in shapeType.avLst) { outString += '' } } outString += '' if (objs_list[i].options) { if (objs_list[i].options.fill) { outString += shapes.createColorElements( objs_list[i].options.fill ) } else if (!objs_list[i].options.disableFillSettings) { outString += '' } // Endif. if (objs_list[i].options.line) { var lineAttr = '' if (objs_list[i].options.line_size) { lineAttr += ' w="' + objs_list[i].options.line_size * 12700 + '"' } // Endif. // cmpd="dbl" outString += '' outString += shapes.createColorElements( objs_list[i].options.line ) if (objs_list[i].options.line_head) { outString += '' } // Endif. if (objs_list[i].options.line_tail) { outString += '' } // Endif. outString += '' } // Endif. } else if (!objs_list[i].options.disableFillSettings) { outString += '' } // Endif. if (objs_list[i].options.effects) { for ( var ii = 0, total_size_ii = objs_list[i].options.effects.length; ii < total_size_ii; ii++ ) { switch (objs_list[i].options.effects[ii].type) { case 'outerShadow': effectsList += generateEffects( objs_list[i].options.effects[ii], 'outerShdw' ) break case 'innerShadow': effectsList += generateEffects( objs_list[i].options.effects[ii], 'innerShdw' ) break } // End of switch. } // End of for loop. } // Endif. if (effectsList !== '') { outString += '' + effectsList + '' } // Endif. outString += '' } // Endif. if (objs_list[i].options) { if (objs_list[i].options.align) { switch (objs_list[i].options.align) { case 'right': moreStylesAttr += ' algn="r"' break case 'center': moreStylesAttr += ' algn="ctr"' break case 'justify': moreStylesAttr += ' algn="just"' break } // End of switch. } // Endif. if (objs_list[i].options.indentLevel > 0) { moreStylesAttr += ' lvl="' + objs_list[i].options.indentLevel + '"' } // Endif. } // Endif. if (moreStyles !== '') { outStyles = '' + moreStyles + '' } else if (moreStylesAttr !== '') { outStyles = '' } // Endif. if (styleData !== '') { outString += '' + styleData + '' } // Endif. if (typeof objs_list[i].text === 'string') { outString += '' + createBodyProperties(objs_list[i].options) + '' + outStyles outString += createXmlSlideTextObject( objs_list[i].options, objs_list[i].text, data.slide, data.slide.getPageNumber(), outStyles ) } else if (typeof objs_list[i].text === 'number') { outString += '' + createBodyProperties(objs_list[i].options) + '' + outStyles outString += createXmlSlideTextObject( objs_list[i].options, objs_list[i].text + '', data.slide, data.slide.getPageNumber(), outStyles ) } else if (objs_list[i].text && objs_list[i].text.length) { var outBodyOpt = createBodyProperties(objs_list[i].options) outString += '' + outBodyOpt + '' + outStyles for ( var j = 0, total_size_j = objs_list[i].text.length; j < total_size_j; j++ ) { if ( typeof objs_list[i].text[j] === 'object' && objs_list[i].text[j].text ) { outString += createXmlSlideTextObject( objs_list[i].text[j].options || objs_list[i].options, objs_list[i].text[j].text, data.slide, outBodyOpt, outStyles, data.slide.getPageNumber() ) } else if (typeof objs_list[i].text[j] === 'string') { outString += createXmlSlideTextObject( objs_list[i].options, objs_list[i].text[j], data.slide, outBodyOpt, outStyles, data.slide.getPageNumber() ) } else if (typeof objs_list[i].text[j] === 'number') { outString += createXmlSlideTextObject( objs_list[i].options, objs_list[i].text[j] + '', data.slide, outBodyOpt, outStyles, data.slide.getPageNumber() ) } else if ( typeof objs_list[i].text[j] === 'object' && objs_list[i].text[j].field ) { outString += createXmlSlideTextObject( objs_list[i].options, objs_list[i].text[j], data.slide, outBodyOpt, outStyles, data.slide.getPageNumber() ) } // Endif. } // Endif. } else if (typeof objs_list[i].text === 'object') { if (!objs_list[i].text) { objs_list[i].text = {} } // Endif. if (!objs_list[i].text.field) { objs_list[i].text.field = '' } // Endif. outString += '' + createBodyProperties(objs_list[i].options) + '' + outStyles outString += createXmlSlideTextObject( objs_list[i].options, objs_list[i].text, data.slide, data.slide.getPageNumber(), outStyles ) } // Endif. // We must add that at the end of every paragraph with text: if (typeof objs_list[i].text !== 'undefined') { var font_size = '' if (objs_list[i].options && objs_list[i].options.font_size) { font_size = ' sz="' + objs_list[i].options.font_size + '00"' } // Endif. outString += '' } // Endif. outString += objs_list[i].type === 'cxn' ? '' : '' /* eslint-disable indent */ break // Table: /* case 'table': outString += '' outString += '' outString += '' if ( objs_list[i].options && objs_list[i].options.tableStyleId ) { outString += objs_list[i].options.tableStyleId } else { outString += '{073A0DAA-6AF3-43AB-8588-CEC1D06C72B9}' } // Endif. outString += '' // outString += '' // objs_list[i].options // objs_list[i].rows[][].text // BMK_TODO: break */ /* eslint-enable indent */ // Image: case 'image': // psv 2015-01-21 Manually copied this section from https://github.com/Ziv-Barber/officegen/pull/39/files?w=1 // outString += '' var parts = [] parts.push( '' ) } else { parts.push('/>') } // Endif. parts.push( '' ) outString += parts.join('') // -- psv 2015-01-21 end merge break // Paragraph: case 'p': if (shapeType == null) { shapeType = shapes.getShapeInfo(null) } // Endif. outString += '' outString += '' outString += '' outString += '' outString += '' if (objs_list[i].options) { if (objs_list[i].options.fill) { outString += shapes.createColorElements(objs_list[i].options.fill) } else if (!objs_list[i].options.disableFillSettings) { outString += '' } // Endif. if (objs_list[i].options.line) { outString += '' outString += shapes.createColorElements(objs_list[i].options.line) if (objs_list[i].options.line_head) { outString += '' } // Endif. if (objs_list[i].options.line_tail) { outString += '' } // Endif. outString += '' } // Endif. } else if (!objs_list[i].options.disableFillSettings) { outString += '' } // Endif. outString += '' if (styleData !== '') { outString += '' + styleData + '' } // Endif. outString += '' // Add all the paragraph objects: outString = createXmlSlideParagraph( outString, objs_list[i].data, objs_list[i].options, data.slide ) outString += '' outString += '' break // Custom xml code: case 'xml': // NOTE: While it allowing you to add any xml code directly, please try to use it only for custom layouts. outString += objs_list[i].data break } // End of switch. } // End of for loop. if (!makeOnlyObjects) { outString += '' if (timingData !== '') { outString += '' + timingData + '' } // Endif. outString += '' } // Endif. return outString } /** * Generate the extended attributes file (app) for PPTX/PPSX documents. * * @param {object} data Ignored by this callback function. * @return Text string. */ function cbMakePptxApp(data) { var slidesCount = gen_private.pages.length var userName = genobj.options.author || genobj.options.creator || 'officegen' var outString = gen_private.plugs.type.msoffice.cbMakeMsOfficeBasicXml(data) + '00Microsoft Office PowerPointOn-screen Show (4:3)0' + slidesCount + '000falseTheme1Slide Titles' + slidesCount + 'Office Theme' for ( var i = 0, total_size = gen_private.pages.length; i < total_size; i++ ) { outString += '' + encodeHTML(gen_private.pages[i].slide.name) + '' } // End of for loop. outString += '' + userName + 'falsefalsefalse12.0000' return outString } /** * Create XML string for a chart description * * @param {object} chartInfo * @return Text Xml string. */ function cbMakeCharts(chartInfo) { var chart = new OfficeChart(chartInfo) return chart.toXML() } function cbMakeChartDataExcel(data) { // PSV not sure why this method is here. Presumably vestigial from earlier code. } // Prepare genobj for MS-Office: msdoc.makemsdoc(genobj, new_type, options, gen_private, type_info) gen_private.plugs.type.msoffice.makeOfficeGenerator('ppt', 'presentation', {}) gen_private.features.page_name = 'slides' // This document type must have pages. gen_private.plugs.type.msoffice.addInfoType( 'dc:title', '', 'title', 'setDocTitle' ) genobj.on('beforeGen', cbPreparePptxToGenerate) var type_of_main_doc = 'slideshow' if (new_type !== 'ppsx') { type_of_main_doc = 'presentation' } // Endif. // Create the plugins manager: var plugsmanObj = new docplugman( genobj, gen_private, 'pptx', setDefaultDocValues ) // We'll register now any specific PowerPoint based plugin that we want to use: plugsmanObj.plugsList.push(new plugWidescreen(plugsmanObj)) plugsmanObj.plugsList.push(new plugSpeakernotes(plugsmanObj)) plugsmanObj.plugsList.push(new plugLayouts(plugsmanObj)) // BMK_PPTX_PLUG: // Dynamic loading of additional plugins requested by the user: if ( options.extraPlugs && typeof options.extraPlugs === 'object' && options.extraPlugs.forEach ) { options.extraPlugs.forEach(function(value) { var newPlug if (value) { if (typeof value === 'function') { // You already loaded the plugin: newPlug = value } else if (typeof value === 'string') { // We need to load the plugin: newPlug = require('./' + value) } // Endif. } // Endif. plugsmanObj.plugsList.push(new newPlug(plugsmanObj)) }) } // Endif. // Save some methods for the plugins: genobj.cbMakePptxLayout1 = cbMakePptxLayout1 genobj.cbMakePptxSlide = cbMakePptxSlide genobj.createFieldText = createFieldText genobj.cMakePptxOutTextP = createXmlSlideParagraph gen_private.type.msoffice.files_list.push( { name: '/ppt/slideMasters/slideMaster1.xml', type: 'application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml', clear: 'type' }, { name: '/ppt/presProps.xml', type: 'application/vnd.openxmlformats-officedocument.presentationml.presProps+xml', clear: 'type' }, { name: '/ppt/presentation.xml', type: 'application/vnd.openxmlformats-officedocument.presentationml.' + type_of_main_doc + '.main+xml', clear: 'type' }, { name: '/ppt/slideLayouts/slideLayout1.xml', type: 'application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml', clear: 'type' }, { name: '/ppt/tableStyles.xml', type: 'application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml', clear: 'type' }, { name: '/ppt/viewProps.xml', type: 'application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml', clear: 'type' } ) genobj.slideMasterRels = [ { type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout', target: '../slideLayouts/slideLayout1.xml' }, { type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme', target: '../theme/theme1.xml' } ] gen_private.plugs.intAddAnyResourceToParse( 'ppt\\presProps.xml', 'buffer', null, cbMakePptxPresProps, true ) gen_private.plugs.intAddAnyResourceToParse( 'ppt\\tableStyles.xml', 'buffer', null, cbMakePptxStyles, true ) gen_private.plugs.intAddAnyResourceToParse( 'ppt\\viewProps.xml', 'buffer', genobj, cbMakePptxViewProps, true ) gen_private.plugs.intAddAnyResourceToParse( 'ppt\\presentation.xml', 'buffer', null, cbMakePptxPresentation, true ) gen_private.plugs.intAddAnyResourceToParse( 'ppt\\slideLayouts\\slideLayout1.xml', 'buffer', null, cbMakePptxLayout1, true ) gen_private.plugs.intAddAnyResourceToParse( 'ppt\\slideLayouts\\_rels\\slideLayout1.xml.rels', 'buffer', [ { type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster', target: '../slideMasters/slideMaster1.xml' } ], gen_private.plugs.type.msoffice.cbMakeRels, true ) // Slides master: gen_private.plugs.intAddAnyResourceToParse( 'ppt\\slideMasters\\slideMaster1.xml', 'buffer', null, cbMakePptxSlideMasters, true ) gen_private.plugs.intAddAnyResourceToParse( 'ppt\\slideMasters\\_rels\\slideMaster1.xml.rels', 'buffer', genobj.slideMasterRels, gen_private.plugs.type.msoffice.cbMakeRels, true ) gen_private.plugs.intAddAnyResourceToParse( 'docProps\\app.xml', 'buffer', null, cbMakePptxApp, true ) gen_private.type.msoffice.rels_app.push({ type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster', target: 'slideMasters/slideMaster1.xml', clear: 'type' }) gen_private.plugs.intAddAnyResourceToParse( 'ppt\\_rels\\presentation.xml.rels', 'buffer', gen_private.type.msoffice.rels_app, gen_private.plugs.type.msoffice.cbMakeRels, true ) // ----- API for PowerPoint documents: ----- /** * Create a new slide. * * This method creating a new slide inside the presentation. * * @param {object} slideOptions Extra options how to create the new slide. * @return The new slide object. */ genobj.makeNewSlide = function(slideOptions) { var pageNumber = gen_private.pages.length var slideObj = { show: true } // The slide object that the user will use. if (!slideOptions || typeof slideOptions !== 'object') { slideOptions = {} } // Endif. if (!slideOptions.basedLayout) { slideOptions.basedLayout = 1 } // Endif. gen_private.pages[pageNumber] = {} gen_private.pages[pageNumber].slide = slideObj gen_private.pages[pageNumber].data = [] gen_private.pages[pageNumber].rels = [ { type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout', target: '../slideLayouts/slideLayout' + slideOptions.basedLayout + '.xml', clear: 'data' } ] gen_private.type.msoffice.files_list.push({ name: '/ppt/slides/slide' + (pageNumber + 1) + '.xml', type: 'application/vnd.openxmlformats-officedocument.presentationml.slide+xml', clear: 'data' }) gen_private.type.msoffice.rels_app.push({ type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', target: 'slides/slide' + (pageNumber + 1) + '.xml', clear: 'data' }) slideObj.getPageNumber = function() { return pageNumber } slideObj.getRelFile = function() { return gen_private.pages[pageNumber].rels } slideObj.name = 'Slide ' + (pageNumber + 1) /** * ???. * * @param {object} prgObj Paragraph object. */ function addParagraphApiForBasicOpt(prgObj) { if (!prgObj.api) { prgObj.api = {} } // Endif. prgObj.api.options = prgObj.options } /** * ???. * * @param {object} prgObj Paragraph object. */ function addParagraphApiForEffects(prgObj) { if (!prgObj.api) { prgObj.api = {} } // Endif. /** * ???. * * @param {object} inType ???. * @param {object} inAlign ???. * @param {object} inColor ???. * @param {object} inTransparency ???. * @param {object} inSize ???. * @param {object} inBlur ???. * @param {object} inAngle ???. * @param {object} inDistance ???. */ prgObj.api.setShadowEffect = function( inType, inAlign, inColor, inTransparency, inSize, inBlur, inAngle, inDistance ) { if (!prgObj.options.effects) { prgObj.options.effects = [] } var newEffect = { type: inType, align: inAlign, color: inColor, transparency: inTransparency, size: inSize, blur: inBlur, angle: inAngle, distance: inDistance } prgObj.options.effects.push(newEffect) } } // Added 2015-01-02 PSV as a way for user to modify chart properties before generating the XML string: slideObj.createChart = function(chartInfo) { return new OfficeChart(chartInfo) } // Added 2015-01-08 PSV following model of addChart: slideObj.addTable = function(data, options) { var objNumber = gen_private.pages[pageNumber].data.length gen_private.pages[pageNumber].data[objNumber] = { type: 'table', data: data, options: options || {} // right now this isn't yet used } addParagraphApiForBasicOpt(gen_private.pages[pageNumber].data[objNumber]) addParagraphApiForEffects(gen_private.pages[pageNumber].data[objNumber]) return gen_private.pages[pageNumber].data[objNumber].api } /** * Generate the chart based on input data. * * @param {object} renderType should belong to: 'column', 'pie' * @param {object} data a JSON object with follow the following format * { * title: 'eSurvey chart', * data: [ * { * name: 'Income', * labels: ['2005', '2006', '2007', '2008', '2009'], * values: [23.5, 26.2, 30.1, 29.5, 24.6] * }, * { * name: 'Expense', * labels: ['2005', '2006', '2007', '2008', '2009'], * values: [18.1, 22.8, 23.9, 25.1, 25] * } * ] * } * @author vtloc * @date 2014Jan09 */ slideObj.addChart = function(data, callback, errorCallback) { var objNumber = gen_private.pages[pageNumber].data.length GLOBAL_CHART_COUNT += 1 gen_private.pages[pageNumber].data[objNumber] = {} gen_private.pages[pageNumber].data[objNumber].type = 'chart' gen_private.pages[pageNumber].data[objNumber].renderType = 'column' gen_private.pages[pageNumber].data[objNumber].title = data['title'] gen_private.pages[pageNumber].data[objNumber].data = data['data'] gen_private.pages[pageNumber].data[objNumber].options = data // include generic options like x,y,cx,cy, etc. // data['renderType'] = renderType // First, generate a temporatory excel file for storing the chart's data var workbook = excelbuilder.createWorkbook( genobj.options.tempDir, 'sample' + GLOBAL_CHART_COUNT + '.xlsx' ) // Create a new worksheet with 10 columns and 12 rows // number of columns: data['data'].length+1 -> equaly number of series // number of rows: data['data'][0].values.length+1 var sheet1 = workbook.createSheet( 'Sheet1', data['data'].length + 1, data['data'][0].values.length + 1 ) var headerrow = 1 // Write header using serie name: for (var j = 0; j < data['data'].length; j++) { sheet1.set(j + 2, headerrow, data['data'][j].name) } // End of for loop. // Write category column in the first column: for (j = 0; j < data['data'][0].labels.length; j++) { sheet1.set(1, j + 2, data['data'][0].labels[j]) } // End of for loop. // For each serie, write out values in its row: for (var i = 0; i < data['data'].length; i++) { for (j = 0; j < data['data'][i].values.length; j++) { // col i+2 // row j+1 sheet1.set(i + 2, j + 2, data['data'][i].values[j]) } // End of for loop. } // End of for loop. // Fill some data // Save it var localEmbeddingExcelFile = 'ppt\\embeddings\\Microsoft_Excel_Worksheet' + GLOBAL_CHART_COUNT + '.xlsx' var tmpExcelFile = genobj.options.tempDir + 'sample' + GLOBAL_CHART_COUNT + '.xlsx' // will copy the tmpExcelFile into localEmbeddingExcelFile gen_private.plugs.intAddAnyResourceToParse( localEmbeddingExcelFile, 'file', tmpExcelFile, cbMakeChartDataExcel, false, true ) gen_private.plugs.intAddAnyResourceToParse( 'ppt\\charts\\chart' + GLOBAL_CHART_COUNT + '.xml', 'buffer', data, cbMakeCharts, true ) gen_private.plugs.intAddAnyResourceToParse( 'ppt\\charts\\_rels\\chart' + GLOBAL_CHART_COUNT + '.xml.rels', 'buffer', [ { type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/package', target: '../embeddings/Microsoft_Excel_Worksheet' + GLOBAL_CHART_COUNT + '.xlsx' } ], gen_private.plugs.type.msoffice.cbMakeRels, true ) gen_private.type.msoffice.files_list.push( { name: '/ppt/charts/chart' + GLOBAL_CHART_COUNT + '.xml', type: 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml', clear: 'type' }, { name: '/ppt/embeddings/Microsoft_Excel_Worksheet' + GLOBAL_CHART_COUNT + '.xlsx', type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', ext: 'xlsx', clear: 'type' } ) gen_private.pages[pageNumber].rels.push({ type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart', target: '../charts/chart' + GLOBAL_CHART_COUNT + '.xml', clear: 'data' }) workbook.save(function(err) { if (err) { workbook.cancel() } else { callback() } }) } /** * ???. * * @param {object} text The text to add. * @param {object} opt ???. * @param {object} y_pos Y position. * @param {object} x_size X size. * @param {object} y_size Y size. * @param {object} opt_b ???. */ slideObj.addText = function(text, opt, y_pos, x_size, y_size, opt_b) { var objNumber = gen_private.pages[pageNumber].data.length gen_private.pages[pageNumber].data[objNumber] = {} gen_private.pages[pageNumber].data[objNumber].type = 'text' gen_private.pages[pageNumber].data[objNumber].text = text || '' gen_private.pages[pageNumber].data[objNumber].options = typeof opt === 'object' ? opt : {} if (typeof opt === 'string') { gen_private.pages[pageNumber].data[objNumber].options.color = opt } else if (typeof opt !== 'object' && typeof y_pos !== 'undefined') { gen_private.pages[pageNumber].data[objNumber].options.x = opt gen_private.pages[pageNumber].data[objNumber].options.y = y_pos if (typeof x_size !== 'undefined' && typeof y_size !== 'undefined') { gen_private.pages[pageNumber].data[objNumber].options.cx = x_size gen_private.pages[pageNumber].data[objNumber].options.cy = y_size } // Endif. } // Endif. var attrname if (typeof opt_b === 'object') { for (attrname in opt_b) { gen_private.pages[pageNumber].data[objNumber].options[attrname] = opt_b[attrname] } } else if (typeof x_size === 'object' && typeof y_size === 'undefined') { for (attrname in x_size) { gen_private.pages[pageNumber].data[objNumber].options[attrname] = x_size[attrname] } } // Endif. addParagraphApiForBasicOpt(gen_private.pages[pageNumber].data[objNumber]) addParagraphApiForEffects(gen_private.pages[pageNumber].data[objNumber]) return gen_private.pages[pageNumber].data[objNumber].api } /** * ??? * * @param {string} shape ???. * @param {object} opt ???. * @param {number} y_pos ???. * @param {number} x_size ???. * @param {number} y_size ???. * @param {object} opt_b ???. */ slideObj.addShape = function(shape, opt, y_pos, x_size, y_size, opt_b) { var objNumber = gen_private.pages[pageNumber].data.length gen_private.pages[pageNumber].data[objNumber] = {} gen_private.pages[pageNumber].data[objNumber].type = 'text' gen_private.pages[pageNumber].data[objNumber].options = typeof opt === 'object' ? opt : {} gen_private.pages[pageNumber].data[objNumber].options.shape = shape if (typeof opt === 'string') { gen_private.pages[pageNumber].data[objNumber].options.color = opt } else if (typeof opt !== 'object' && typeof y_pos !== 'undefined') { gen_private.pages[pageNumber].data[objNumber].options.x = opt gen_private.pages[pageNumber].data[objNumber].options.y = y_pos if (typeof x_size !== 'undefined' && typeof y_size !== 'undefined') { gen_private.pages[pageNumber].data[objNumber].options.cx = x_size gen_private.pages[pageNumber].data[objNumber].options.cy = y_size } // Endif. } // Endif. var attrname if (typeof opt_b === 'object') { for (attrname in opt_b) { gen_private.pages[pageNumber].data[objNumber].options[attrname] = opt_b[attrname] } } else if (typeof x_size === 'object' && typeof y_size === 'undefined') { for (attrname in x_size) { gen_private.pages[pageNumber].data[objNumber].options[attrname] = x_size[attrname] } } // Endif. addParagraphApiForBasicOpt(gen_private.pages[pageNumber].data[objNumber]) addParagraphApiForEffects(gen_private.pages[pageNumber].data[objNumber]) return gen_private.pages[pageNumber].data[objNumber].api } /** * ???. * * @param {object} image_path ???. * @param {object} opt ???. * @param {object} y_pos ???. * @param {object} x_size ???. * @param {object} y_size ???. * @param {object} image_format_type ???. */ slideObj.addImage = function( image_path, opt, y_pos, x_size, y_size, image_format_type ) { var objNumber = gen_private.pages[pageNumber].data.length var image_type = typeof image_format_type === 'string' ? image_format_type : 'png' var defWidth var defHeight = 0 if (typeof image_path === 'string') { var ret_data = fast_image_size(image_path) if (ret_data.type === 'unknown') { var image_ext = path.extname(image_path) switch (image_ext) { case '.bmp': image_type = 'bmp' break case '.gif': image_type = 'gif' break case '.jpg': case '.jpeg': image_type = 'jpeg' break case '.emf': image_type = 'emf' break case '.tiff': image_type = 'tiff' break } // End of switch. } else { if (ret_data.width) { defWidth = ret_data.width } // Endif. if (ret_data.height) { defHeight = ret_data.height } // Endif. image_type = ret_data.type if (image_type === 'jpg') { image_type = 'jpeg' } // Endif. } // Endif. } // Endif. gen_private.pages[pageNumber].data[objNumber] = {} gen_private.pages[pageNumber].data[objNumber].type = 'image' gen_private.pages[pageNumber].data[objNumber].image = image_path gen_private.pages[pageNumber].data[objNumber].options = typeof opt === 'object' ? opt : {} if ( !gen_private.pages[pageNumber].data[objNumber].options.cx && defWidth ) { gen_private.pages[pageNumber].data[objNumber].options.cx = defWidth } // Endif. if ( !gen_private.pages[pageNumber].data[objNumber].options.cy && defHeight ) { gen_private.pages[pageNumber].data[objNumber].options.cy = defHeight } // Endif. var image_id = gen_private.type.msoffice.src_files_list.indexOf( image_path ) var image_rel_id = -1 if (image_id >= 0) { for ( var j = 0, total_size_j = gen_private.pages[pageNumber].rels.length; j < total_size_j; j++ ) { if ( gen_private.pages[pageNumber].rels[j].target === '../media/image' + (image_id + 1) + '.' + image_type ) { image_rel_id = j + 1 } // Endif. } // Endif. } else { image_id = gen_private.type.msoffice.src_files_list.length gen_private.type.msoffice.src_files_list[image_id] = image_path gen_private.plugs.intAddAnyResourceToParse( 'ppt\\media\\image' + (image_id + 1) + '.' + image_type, typeof image_path === 'string' ? 'file' : 'stream', image_path, null, false ) } // Endif. if (image_rel_id === -1) { image_rel_id = gen_private.pages[pageNumber].rels.length + 1 gen_private.pages[pageNumber].rels.push({ type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', target: '../media/image' + (image_id + 1) + '.' + image_type, clear: 'data' }) } // Endif. // -- psv 2015-01-21 Manually copied change below from https://github.com/Ziv-Barber/officegen/pull/39/files?w=1 if ((opt || {}).link) { var link_rel_id = gen_private.pages[pageNumber].rels.length + 1 gen_private.pages[pageNumber].rels.push({ type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', target: opt.link, targetMode: 'External' }) gen_private.pages[pageNumber].data[objNumber].link_rel_id = link_rel_id } // Endif. // -- psv 2015-01-21 end edits gen_private.pages[pageNumber].data[objNumber].image_id = image_id gen_private.pages[pageNumber].data[objNumber].rel_id = image_rel_id if (typeof opt === 'string') { gen_private.pages[pageNumber].data[objNumber].options.color = opt } else if (typeof opt !== 'object' && typeof y_pos !== 'undefined') { gen_private.pages[pageNumber].data[objNumber].options.x = opt gen_private.pages[pageNumber].data[objNumber].options.y = y_pos if (typeof x_size !== 'undefined' && typeof y_size !== 'undefined') { gen_private.pages[pageNumber].data[objNumber].options.cx = x_size gen_private.pages[pageNumber].data[objNumber].options.cy = y_size } // Endif. } // Endif. addParagraphApiForBasicOpt(gen_private.pages[pageNumber].data[objNumber]) addParagraphApiForEffects(gen_private.pages[pageNumber].data[objNumber]) return gen_private.pages[pageNumber].data[objNumber].api } /** * ???. * * @param {object} text ???. * @param {object} opt ???. * @param {object} y_pos ???. * @param {object} x_size ???. * @param {object} y_size ???. * @param {object} opt_b ???. */ slideObj.addP = function(text, opt, y_pos, x_size, y_size, opt_b) { var objNumber = gen_private.pages[pageNumber].data.length gen_private.pages[pageNumber].data[objNumber] = {} gen_private.pages[pageNumber].data[objNumber].type = 'p' gen_private.pages[pageNumber].data[objNumber].data = [] gen_private.pages[pageNumber].data[objNumber].options = typeof opt === 'object' ? opt : {} if (typeof opt === 'string') { gen_private.pages[pageNumber].data[objNumber].options.color = opt } else if (typeof opt !== 'object' && typeof y_pos !== 'undefined') { gen_private.pages[pageNumber].data[objNumber].options.x = opt gen_private.pages[pageNumber].data[objNumber].options.y = y_pos if (typeof x_size !== 'undefined' && typeof y_size !== 'undefined') { gen_private.pages[pageNumber].data[objNumber].options.cx = x_size gen_private.pages[pageNumber].data[objNumber].options.cy = y_size } // Endif. } // Endif. var attrname if (typeof opt_b === 'object') { for (attrname in opt_b) { gen_private.pages[pageNumber].data[objNumber].options[attrname] = opt_b[attrname] } } else if (typeof x_size === 'object' && typeof y_size === 'undefined') { for (attrname in x_size) { gen_private.pages[pageNumber].data[objNumber].options[attrname] = x_size[attrname] } } // Endif. // BMK_TODO: return gen_private.pages[pageNumber].data[objNumber].data } /** * API to add direct custom xml code into the slide. * * @param {object} code The xml code to add. */ slideObj.addDirectXmlCode = function(code) { var objNumber = gen_private.pages[pageNumber].data.length gen_private.pages[pageNumber].data[objNumber] = {} gen_private.pages[pageNumber].data[objNumber].type = 'xml' gen_private.pages[pageNumber].data[objNumber].data = code gen_private.pages[pageNumber].data[objNumber].options = {} return gen_private.pages[pageNumber].data[objNumber].data } slideObj.addDateToHeader = function() { if (!gen_private.pages[pageNumber].header) { gen_private.pages[pageNumber].header = {} } // Endif. // gen_private.pages[pageNumber] // // BMK_TODO: } gen_private.plugs.intAddAnyResourceToParse( 'ppt\\slides\\slide' + (pageNumber + 1) + '.xml', 'buffer', gen_private.pages[pageNumber], cbMakePptxSlide, false ) gen_private.plugs.intAddAnyResourceToParse( 'ppt\\slides\\_rels\\slide' + (pageNumber + 1) + '.xml.rels', 'buffer', gen_private.pages[pageNumber].rels, gen_private.plugs.type.msoffice.cbMakeRels, false ) // Signal to the plugins about a new slide: gen_private.features.type.pptx.emitEvent('newPage', { genobj: genobj, page: slideObj, pageData: gen_private.pages[pageNumber], pageNumber: pageNumber, slideOptions: slideOptions }) return slideObj } /** * Change the master slide. * * This method changing the master slide. Temporary code - don't use this method in real production code! * * @param {string} xmlCode - Master slide xml code. */ genobj.setMasterSlideXml = function(xmlCode) { gen_private.masterSlideXmlCode = xmlCode } // Tell all the features (plugins) to add extra API: gen_private.features.type.pptx.emitEvent('makeDocApi', genobj) return this } baseobj.plugins.registerDocType( 'pptx', makePptx, {}, baseobj.docType.PRESENTATION, 'Microsoft PowerPoint Document' ) baseobj.plugins.registerDocType( 'ppsx', makePptx, {}, baseobj.docType.PRESENTATION, 'Microsoft PowerPoint Slideshow Document' )