/* * Copyright (C) Ascensio System SIA, 2009-2026 * * This program is a free software product. You can redistribute it and/or * modify it under the terms of the GNU Affero General Public License (AGPL) * version 3 as published by the Free Software Foundation, together with the * additional terms provided in the LICENSE file. * * This program is distributed WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For * details, see the GNU AGPL at: https://www.gnu.org/licenses/agpl-3.0.html * * You can contact Ascensio System SIA by email at info@onlyoffice.com * or by postal mail at 20A-6 Ernesta Birznieka-Upisha Street, Riga, * LV-1050, Latvia, European Union. * * The interactive user interfaces in modified versions of the Program * are required to display Appropriate Legal Notices in accordance with * Section 5 of the GNU AGPL version 3. * * No trademark rights are granted under this License. * * All non-code elements of the Product, including illustrations, * icon sets, and technical writing content, are licensed under the * Creative Commons Attribution-ShareAlike 4.0 International License: * https://creativecommons.org/licenses/by-sa/4.0/legalcode * * This license applies only to such non-code elements and does not * modify or replace the licensing terms applicable to the Program's * source code, which remains licensed under the GNU Affero General * Public License v3. * * SPDX-License-Identifier: AGPL-3.0-only */ #include "./editors.h" #include "../common/StringBuilder.h" namespace NSDoctRenderer { namespace { bool AppendScript(NSStringUtils::CStringBuilderA* builder, const std::wstring& path, const std::string& header = "", const std::string& footer = "") { NSFile::CFileBinary oFile; if (!oFile.OpenFile(path)) return false; int size = (int)oFile.GetFileSize(); if (size < 3) return false; BYTE* pData = new BYTE[size]; DWORD dwReadSize = 0; oFile.ReadFile(pData, (DWORD)size, dwReadSize); oFile.CloseFile(); int nOffset = 0; if (pData[0] == 0xEF && pData[1] == 0xBB && pData[2] == 0xBF) nOffset = 3; if (!header.empty()) builder->WriteString(header); builder->WriteString((char*)(pData + nOffset), size - nOffset); if (!footer.empty()) builder->WriteString(footer); builder->WriteString("\n\n"); RELEASEARRAYOBJECTS(pData); return true; } bool RunScript(JSSmart& context, JSSmart& try_catch, const std::wstring& path) { NSFile::CFileBinary oFile; if (!oFile.OpenFile(path)) return false; int size = (int)oFile.GetFileSize(); if (size < 3) return false; BYTE* pData = new BYTE[size]; DWORD dwReadSize = 0; oFile.ReadFile(pData, (DWORD)size, dwReadSize); oFile.CloseFile(); int nOffset = 0; if (pData[0] == 0xEF && pData[1] == 0xBB && pData[2] == 0xBF) nOffset = 3; context->runScript(std::string((char*)(pData + nOffset), size - nOffset), try_catch); RELEASEARRAYOBJECTS(pData); return !try_catch->Check(); } std::wstring GetAllScript(NSStringUtils::CStringBuilderA* builder, const DoctRendererEditorType& type, CDoctRendererConfig* config, const bool& isSnapshot = false) { for (std::vector::const_iterator i = config->m_arrFiles.cbegin(); i != config->m_arrFiles.cend(); i++) AppendScript(builder, *i); std::wstring sFontsPath = config->m_strSdkPath + L"/common/libfont/engine"; #ifdef SUPPORT_HARFBUZZ_SHAPER sFontsPath += L"/fonts_native.js"; #else sFontsPath += L"/fonts_ie.js"; #endif std::wstring sCachePath = L""; switch (type) { case DoctRendererEditorType::WORD: { AppendScript(builder, config->m_strSdkPath + L"/word/sdk-all-min.js"); AppendScript(builder, sFontsPath); AppendScript(builder, config->m_strSdkPath + L"/word/sdk-all.js"); sCachePath = config->m_strSdkPath + L"/word/sdk-all"; break; } case DoctRendererEditorType::SLIDE: { AppendScript(builder, config->m_strSdkPath + L"/slide/sdk-all-min.js"); AppendScript(builder, sFontsPath); AppendScript(builder, config->m_strSdkPath + L"/slide/sdk-all.js"); sCachePath = config->m_strSdkPath + L"/slide/sdk-all"; break; } case DoctRendererEditorType::CELL: { AppendScript(builder, config->m_strSdkPath + L"/cell/sdk-all-min.js"); AppendScript(builder, sFontsPath); AppendScript(builder, config->m_strSdkPath + L"/cell/sdk-all.js"); builder->WriteString("\n$.ready();", 11); sCachePath = config->m_strSdkPath + L"/cell/sdk-all"; break; } case DoctRendererEditorType::VISIO: { if (!NSFile::CFileBinary::Exists(config->m_strSdkPath + L"/visio/sdk-all-min.js")) return L""; AppendScript(builder, config->m_strSdkPath + L"/visio/sdk-all-min.js"); AppendScript(builder, sFontsPath); AppendScript(builder, config->m_strSdkPath + L"/visio/sdk-all.js"); sCachePath = config->m_strSdkPath + L"/visio/sdk-all"; break; } case DoctRendererEditorType::PDF: { AppendScript(builder, config->m_strSdkPath + L"/word/sdk-all-min.js"); AppendScript(builder, sFontsPath); AppendScript(builder, config->m_strSdkPath + L"/word/sdk-all.js"); AppendScript(builder, config->m_strSdkPath + L"/pdf/src/engine/drawingfile_native.js"); AppendScript(builder, config->m_strSdkPath + L"/pdf/src/annotations/stamps.json", "window[\"native_pdf_stamps\"]=", ";"); sCachePath = config->m_strSdkPath + L"/pdf/sdk-all"; break; } default: break; } if (!sCachePath.empty()) sCachePath += (isSnapshot ? L".bin" : L".cache"); return sCachePath; } std::wstring GetSnapshotPath(const DoctRendererEditorType& type, CDoctRendererConfig* config) { std::wstring sCachePath = L""; switch (type) { case DoctRendererEditorType::WORD: { return config->m_strSdkPath + L"/word/sdk-all.bin"; } case DoctRendererEditorType::SLIDE: { return config->m_strSdkPath + L"/slide/sdk-all.bin"; } case DoctRendererEditorType::CELL: { return config->m_strSdkPath + L"/cell/sdk-all.bin"; } case DoctRendererEditorType::VISIO: { return config->m_strSdkPath + L"/visio/sdk-all.bin"; } case DoctRendererEditorType::PDF: { return config->m_strSdkPath + L"/pdf/sdk-all.bin"; } default: break; } return L""; } bool RunEditorFooter(JSSmart& context, JSSmart& try_catch, CDoctRendererConfig* config) { if (!RunScript(context, try_catch, config->m_strAllFonts)) return false; std::string sFooter = "window.InitNativeEditors();"; if (context->isSnapshotUsed()) { sFooter += "\ if (undefined === String.prototype.replaceAll)\ {\ String.prototype.replaceAll = function(str, newStr)\ {\ if (Object.prototype.toString.call(str).toLowerCase() === '[object regexp]')\ return this.replace(str, newStr);\ return this.split(str).join(newStr);\ };\ }\n"; } context->runScript(sFooter, try_catch); return !try_catch->Check(); } } // namespace bool RunEditor(const DoctRendererEditorType& type, JSSmart& context, JSSmart& try_catch, CDoctRendererConfig* config) { if (context->isSnapshotUsed()) return RunEditorFooter(context, try_catch, config); NSStringUtils::CStringBuilderA builder; builder.AddSize(10 * 1024 * 1024); std::wstring sCachePath = GetAllScript(&builder, type, config); if (!sCachePath.empty()) { context->runScript(builder.GetData(), try_catch, config->m_bIsUseCache ? sCachePath : L""); RunEditorFooter(context, try_catch, config); } return !try_catch->Check(); } bool GenerateEditorSnapshot(const DoctRendererEditorType& type, CDoctRendererConfig* config) { NSStringUtils::CStringBuilderA builder; builder.AddSize(10 * 1024 * 1024); std::wstring sCachePath = GetAllScript(&builder, type, config, true); builder.WriteString("delete String.prototype.replaceAll;"); if (sCachePath.empty()) return false; return NSJSBase::CJSContext::generateSnapshot(builder.GetData(), sCachePath); } namespace { JSSmart RunEditorWithSnapshot(const DoctRendererEditorType& type, CDoctRendererConfig* config) { #ifndef V8_SUPPORT_SNAPSHOTS return NULL; #endif std::wstring sCachePath = GetSnapshotPath(type, config); if (!NSFile::CFileBinary::Exists(sCachePath)) return NULL; if (sCachePath.empty()) return NULL; JSSmart context = new NSJSBase::CJSContext(false); context->Initialize(sCachePath); return context; } } // namespace JSSmart CreateEditorContext(const DoctRendererEditorType& type, CDoctRendererConfig* config) { JSSmart context = RunEditorWithSnapshot(type, config); if (context.is_init()) return context; return new NSJSBase::CJSContext(); } } // namespace NSDoctRenderer