|
|
define([
|
|
|
'/api/config',
|
|
|
'/bower_components/nthen/index.js',
|
|
|
'/common/common-util.js',
|
|
|
], function (ApiConfig, nThen, Util) {
|
|
|
var X2T = {};
|
|
|
|
|
|
var CURRENT_VERSION = X2T.CURRENT_VERSION = 'v5';
|
|
|
var debug = function (str) {
|
|
|
if (localStorage.CryptPad_dev !== "1") { return; }
|
|
|
console.debug(str);
|
|
|
};
|
|
|
|
|
|
X2T.start = function () {
|
|
|
var x2tReady = Util.mkEvent(true);
|
|
|
var fetchFonts = function (x2t, obj, cb) {
|
|
|
if (!obj.fonts) { return void cb(); }
|
|
|
var path = ApiConfig.httpSafeOrigin + '/common/onlyoffice/'+CURRENT_VERSION+'/fonts/';
|
|
|
var ver = '?' + ApiConfig.requireConf.urlArgs;
|
|
|
var fonts = obj.fonts;
|
|
|
var files = obj.fonts_files;
|
|
|
var suffixes = {
|
|
|
indexR: '',
|
|
|
indexB: '_Bold',
|
|
|
indexBI: '_Bold_Italic',
|
|
|
indexI: '_Italic',
|
|
|
};
|
|
|
nThen(function (waitFor) {
|
|
|
fonts.forEach(function (font) {
|
|
|
// Check if the font is already loaded
|
|
|
if (!font.NeedStyles) { return; }
|
|
|
// Pick the variants we need (regular, bold, italic)
|
|
|
['indexR', 'indexB', 'indexI', 'indexBI'].forEach(function (k) {
|
|
|
if (typeof(font[k]) !== "number" || font[k] === -1) { return; } // No matching file
|
|
|
var file = files[font[k]];
|
|
|
|
|
|
var name = font.Name + suffixes[k] + '.ttf';
|
|
|
Util.fetch(path + file.Id + ver, waitFor(function (err, buffer) {
|
|
|
if (buffer) {
|
|
|
x2t.FS.writeFile('/working/fonts/' + name, buffer);
|
|
|
}
|
|
|
}));
|
|
|
});
|
|
|
});
|
|
|
}).nThen(function () {
|
|
|
cb();
|
|
|
});
|
|
|
};
|
|
|
var x2tInitialized = false;
|
|
|
var x2tInit = function(x2t) {
|
|
|
debug("x2t mount");
|
|
|
// x2t.FS.mount(x2t.MEMFS, {} , '/');
|
|
|
x2t.FS.mkdir('/working');
|
|
|
x2t.FS.mkdir('/working/media');
|
|
|
x2t.FS.mkdir('/working/fonts');
|
|
|
x2t.FS.mkdir('/working/themes');
|
|
|
x2tInitialized = true;
|
|
|
x2tReady.fire();
|
|
|
debug("x2t mount done");
|
|
|
};
|
|
|
var getX2T = function (cb) {
|
|
|
// Perform the x2t conversion
|
|
|
require(['/common/onlyoffice/x2t/x2t.js'], function() { // FIXME why does this fail without an access-control-allow-origin header?
|
|
|
var x2t = window.Module;
|
|
|
x2t.run();
|
|
|
if (x2tInitialized) {
|
|
|
debug("x2t runtime already initialized");
|
|
|
return void x2tReady.reg(function () {
|
|
|
cb(x2t);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
x2t.onRuntimeInitialized = function() {
|
|
|
debug("x2t in runtime initialized");
|
|
|
// Init x2t js module
|
|
|
x2tInit(x2t);
|
|
|
x2tReady.reg(function () {
|
|
|
cb(x2t);
|
|
|
});
|
|
|
};
|
|
|
});
|
|
|
};
|
|
|
|
|
|
var getFormatId = function (ext) {
|
|
|
// Sheets
|
|
|
if (ext === 'xlsx') { return 257; }
|
|
|
if (ext === 'xls') { return 258; }
|
|
|
if (ext === 'ods') { return 259; }
|
|
|
if (ext === 'csv') { return 260; }
|
|
|
if (ext === 'pdf') { return 513; }
|
|
|
// Docs
|
|
|
if (ext === 'docx') { return 65; }
|
|
|
if (ext === 'doc') { return 66; }
|
|
|
if (ext === 'odt') { return 67; }
|
|
|
if (ext === 'txt') { return 69; }
|
|
|
if (ext === 'html') { return 70; }
|
|
|
|
|
|
// Slides
|
|
|
if (ext === 'pptx') { return 129; }
|
|
|
if (ext === 'ppt') { return 130; }
|
|
|
if (ext === 'odp') { return 131; }
|
|
|
|
|
|
return;
|
|
|
};
|
|
|
var getFromId = function (ext) {
|
|
|
var id = getFormatId(ext);
|
|
|
if (!id) { return ''; }
|
|
|
return '<m_nFormatFrom>'+id+'</m_nFormatFrom>';
|
|
|
};
|
|
|
var getToId = function (ext) {
|
|
|
var id = getFormatId(ext);
|
|
|
if (!id) { return ''; }
|
|
|
return '<m_nFormatTo>'+id+'</m_nFormatTo>';
|
|
|
};
|
|
|
|
|
|
// Sanitize file names
|
|
|
var illegalRe = /[\/\?<>\\:\*\|"]/g;
|
|
|
var controlRe = /[\x00-\x1f\x80-\x9f]/g;
|
|
|
var reservedRe = /^\.+$/;
|
|
|
var safeRe = /[&'%!"{}[\]]/g;
|
|
|
var sanitize = function (input) {
|
|
|
if (typeof input !== 'string') { return 'file'; }
|
|
|
var s = input.split('.');
|
|
|
var ext = s.pop() || 'bin';
|
|
|
var name = s.join('');
|
|
|
var replacement = '';
|
|
|
console.error(name);
|
|
|
var sanitized = name
|
|
|
.replace(illegalRe, replacement)
|
|
|
.replace(controlRe, replacement)
|
|
|
.replace(reservedRe, replacement)
|
|
|
.replace(safeRe, replacement);
|
|
|
sanitized = sanitized || 'file';
|
|
|
return sanitized.slice(0, 255) + '.' + ext;
|
|
|
};
|
|
|
|
|
|
var x2tConvertDataInternal = function(x2t, obj) {
|
|
|
var data = obj.data;
|
|
|
var fileName = obj.fileName;
|
|
|
var outputFormat = obj.outputFormat;
|
|
|
var images = obj.images;
|
|
|
debug("Converting Data for " + fileName + " to " + outputFormat);
|
|
|
|
|
|
// PDF
|
|
|
var pdfData = '';
|
|
|
if (outputFormat === "pdf" && typeof(data) === "object" && data.bin && data.buffer) {
|
|
|
// Add conversion rules
|
|
|
pdfData = "<m_bIsNoBase64>false</m_bIsNoBase64>" +
|
|
|
"<m_sFontDir>/working/fonts/</m_sFontDir>";
|
|
|
// writing file to mounted working disk (in memory)
|
|
|
x2t.FS.writeFile('/working/' + fileName, data.bin);
|
|
|
x2t.FS.writeFile('/working/pdf.bin', data.buffer);
|
|
|
} else {
|
|
|
// writing file to mounted working disk (in memory)
|
|
|
x2t.FS.writeFile('/working/' + fileName, data);
|
|
|
}
|
|
|
|
|
|
// Adding images
|
|
|
Object.keys(images || {}).forEach(function (_mediaFileName) {
|
|
|
if (/\.bin$/.test(_mediaFileName)) { return; }
|
|
|
var mediasSources = obj.mediasSources || {};
|
|
|
var mediasData = obj.mediasData || {};
|
|
|
var mediaData = mediasData[_mediaFileName];
|
|
|
var mediaFileName;
|
|
|
if (mediaData) { // Theme image
|
|
|
var path = _mediaFileName.split('/');
|
|
|
mediaFileName = path.pop();
|
|
|
var theme = path[path.indexOf('themes') + 1];
|
|
|
try {
|
|
|
x2t.FS.mkdir('/working/themes/'+theme);
|
|
|
x2t.FS.mkdir('/working/themes/'+theme+'/media');
|
|
|
} catch (e) {
|
|
|
console.warn(e);
|
|
|
}
|
|
|
x2t.FS.writeFile('/working/themes/'+theme+'/media/' + mediaFileName, new Uint8Array(mediaData.content));
|
|
|
debug("Writing media data " + mediaFileName + " at /working/themes/"+theme+"/media/");
|
|
|
return;
|
|
|
}
|
|
|
// mediaData is undefined, check mediasSources
|
|
|
mediaFileName = _mediaFileName.substring(6);
|
|
|
var mediaSource = mediasSources[mediaFileName];
|
|
|
mediaData = mediaSource ? mediasData[mediaSource.src] : undefined;
|
|
|
if (mediaData) {
|
|
|
debug("Writing media data " + mediaFileName);
|
|
|
debug("Data");
|
|
|
var fileData = mediaData.content;
|
|
|
x2t.FS.writeFile('/working/media/' + mediaFileName, new Uint8Array(fileData));
|
|
|
} else {
|
|
|
debug("Could not find media content for " + mediaFileName);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
|
|
|
var inputFormat = fileName.split('.').pop();
|
|
|
|
|
|
var params = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
|
|
|
+ "<TaskQueueDataConvert xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">"
|
|
|
+ "<m_sFileFrom>/working/" + fileName + "</m_sFileFrom>"
|
|
|
+ "<m_sThemeDir>/working/themes</m_sThemeDir>"
|
|
|
+ "<m_sFileTo>/working/" + fileName + "." + outputFormat + "</m_sFileTo>"
|
|
|
+ pdfData
|
|
|
+ getFromId(inputFormat)
|
|
|
+ getToId(outputFormat)
|
|
|
+ "<m_bIsNoBase64>false</m_bIsNoBase64>"
|
|
|
+ "</TaskQueueDataConvert>";
|
|
|
|
|
|
|
|
|
// writing params file to mounted working disk (in memory)
|
|
|
x2t.FS.writeFile('/working/params.xml', params);
|
|
|
try {
|
|
|
// running conversion
|
|
|
x2t.ccall("runX2T", ["number"], ["string"], ["/working/params.xml"]);
|
|
|
} catch (e) {
|
|
|
console.error(e);
|
|
|
return "";
|
|
|
}
|
|
|
|
|
|
// reading output file from working disk (in memory)
|
|
|
var result;
|
|
|
try {
|
|
|
result = x2t.FS.readFile('/working/' + fileName + "." + outputFormat);
|
|
|
} catch (e) {
|
|
|
debug("Failed reading converted file");
|
|
|
return "";
|
|
|
}
|
|
|
return result;
|
|
|
};
|
|
|
|
|
|
var convert = function (obj, cb) {
|
|
|
console.error(obj);
|
|
|
obj.fileName = sanitize(obj.fileName);
|
|
|
getX2T(function (x2t) {
|
|
|
// Fonts
|
|
|
fetchFonts(x2t, obj, function () {
|
|
|
var o = obj.outputFormat;
|
|
|
|
|
|
if (o !== 'pdf') {
|
|
|
// Add intermediary conversion to Microsoft Office format if needed
|
|
|
// (bin to pdf is allowed)
|
|
|
[
|
|
|
// Import from Open Document
|
|
|
{source: '.ods', format: 'xlsx'},
|
|
|
{source: '.odt', format: 'docx'},
|
|
|
{source: '.odp', format: 'pptx'},
|
|
|
// Export to non Microsoft Office
|
|
|
{source: '.bin', type: 'sheet', format: 'xlsx'},
|
|
|
{source: '.bin', type: 'doc', format: 'docx'},
|
|
|
{source: '.bin', type: 'presentation', format: 'pptx'},
|
|
|
].forEach(function (_step) {
|
|
|
if (obj.fileName.endsWith(_step.source) && obj.outputFormat !== _step.format &&
|
|
|
(!_step.type || _step.type === obj.type)) {
|
|
|
obj.outputFormat = _step.format;
|
|
|
obj.data = x2tConvertDataInternal(x2t, obj);
|
|
|
obj.fileName += '.'+_step.format;
|
|
|
}
|
|
|
});
|
|
|
obj.outputFormat = o;
|
|
|
}
|
|
|
|
|
|
var data = x2tConvertDataInternal(x2t, obj);
|
|
|
|
|
|
// Convert to bin -- Import
|
|
|
// We need to extract the images
|
|
|
var images;
|
|
|
if (o === 'bin') {
|
|
|
images = [];
|
|
|
var files = x2t.FS.readdir("/working/media/");
|
|
|
files.forEach(function (file) {
|
|
|
if (file !== "." && file !== "..") {
|
|
|
var fileData = x2t.FS.readFile("/working/media/" + file, {
|
|
|
encoding : "binary"
|
|
|
});
|
|
|
images.push({
|
|
|
name: file,
|
|
|
data: fileData
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
cb({
|
|
|
data: data,
|
|
|
images: images
|
|
|
});
|
|
|
});
|
|
|
});
|
|
|
};
|
|
|
|
|
|
return {
|
|
|
convert: convert
|
|
|
};
|
|
|
};
|
|
|
|
|
|
return X2T;
|
|
|
});
|