ONLYOFFICE. The bare truth about the source code of the cloud office


    In early July, Teamlab was renamed ONLYOFFICE and fully opened the source code of its application, including online document editors, co-editing, Gantt chart and billing functionality.
    For two weeks we received a lot of questions on this topic, so as an employee of the company I’ll undertake to explain what’s what. If you don’t have time to read the post, just download the ONLYOFFICE installation from Sourceforge. Deploy on your server. Done! You have your own cloud office. You are gorgeous.

    Why Teamlab Becomes ONLYOFFICE

    The idea of ​​rebranding has existed for a long time. In our blog we wrote a whole post about why we chose ONLY as the new prefix to our office, however, there were quite specific reasons. Firstly, the name Teamlab has ceased to reflect the idea of ​​the product, because “team” is associated with a small team, while among our customers there are more and more companies of 300 and 400 people. Secondly, besides us, unfortunately, this word was chosen by designers of interactive hangers from Japan.

    Why we opened the source code (Honesty is the best policy because)

    The main reason is, of course, trust. Not only from customers, but also from our partners . In today's situation, we get a lot of questions about the security of corporate data storage, especially from foreign customers. By publishing ONLYOFFICE code on Sourceforge and GitHub , we take another step towards them.

    License Choice

    By publishing the source codes of our product, we pursued several goals:
    • Get the attention of the open source community.
    • To fix the authorship of the product.
    • Strengthen the trust of foreign customers and partners.

    After weighing the pros and cons, we chose AGPL v3 . In a nutshell, it means the following: you can use ONLYOFFICE in your company for free and without restrictions, but if you want to embed our code in your own application, you will have to open the source codes of your product under the same license. More details here .

    What our programmers say

    We pass to the most interesting part of our post. The Open Source version of ONLYOFFICE was getting ready for release for quite some time - a text editor alone contains more than 500,000 lines of code! Of course, we have something to tell about:

    Special attention to Internet Explorer
    The basis of the rasterization algorithm is taken from freetype, as it is a quality, time-tested open font engine. The algorithms themselves have changed a bit so that the execution speed is higher precisely in the browser, since we are writing in javascript.

    But, for example, we had to write the letter cache in two versions: for Internet Explorer and for everyone else. Due to the browser specifics in IE, the first implementation was slow.
    Oleg, Leading Programmer

    What do you know about column widths in Excel?
    There are two main scenarios for using Excel:
    • financial calculations
    • creation of forms (accounts) for further filling and printing.

    A large number of forms were created before the appearance of our table editor, so our task was not only to learn how to open them correctly, but also to correctly calculate the column widths and row heights. Otherwise, when printing, the form might not have been put on the sheet.
    For example, the standard column width in Microsoft Excel is 8.43 characters. It is calculated from the default width of 8 characters, which translates into pixels depending on the Normal style. Then, as it turned out, the resulting number is rounded to the nearest multiple of 8.
    Another feature is the line height. You cannot specify line spacing in Excel, as in Word, for example, so calculating the line height in Excel is different from calculating the line height in Word. For example, a comparison in the Cambria Math 11 font:

    As a result, we implemented the calculation so that not a single character is truncated.
    Alexander, Lead Programmer

    How we linked the dependency tree and topological sorting
    Most spreadsheets somehow contain formulas. To solve the problems of parsing and counting formulas, a parser and a calculator were implemented. Also, when working with formulas, the construction of dependencies between cells deserves special attention. For this purpose, a topological sorting algorithm was used , which makes it possible to build the traversal order of the dependency tree.
    Dmitry, programmer

    Fifteen thousand tests and 80 cars
    To test the ONLYOFFICE installation, we deployed it to ourselves and started the already familiar process. In total, about 15,000 tests run on 80 machines. By the way, we host them now on Digital Ocean and in the near future we will talk about how we transferred the entire test structure there.
    Stas, Head of Testing

    Hopes & Fears
    Are we afraid that someone will borrow our code for personal gain? Not. The use of the ONLYOFFICE code is clearly stated in the license, and here we are protected by international law.
    Do we not think that someone will “pry” and make a similar product? Of course we think. But this will take the guys at least 3 years, so we are not really worried. However, we have long been accustomed to being and surviving in a highly competitive environment.
    Alexandra, Head of Promotion

    What should you build your editor on the canvas?
    Any text editor starts with breaking text into lines and rendering. The first working version consisted of 150 lines of code and was implemented as follows:

    // Класс ParaText
    function ParaText(value)
    {
        this.Value = value;
        this.Type  = para_Text;
        this.Width  = 0;
        this.Height = 0;
    }
    ParaText.prototype.Draw = function(X, Y, Context);
    ParaText.prototype.Measure = function(Context);
     

    Another 140 lines of code
    // Класс ParaSpace
    function ParaSpace()
    {
        this.Type = para_Space;
        this.Width  = 0;
        this.Height = 0;
    }
    ParaSpace.prototype.Draw = function(X,Y, Context);
    ParaSpace.prototype.Measure = function(Context);
    // Класс ParaNewLineRendered
    function ParaNewLineRendered()
    {
        this.Type = para_NewLineRendered;
        this.Width  = 0;
        this.Height = 0;
    }
    ParaNewLineRendered.prototype.Draw = function();
    ParaNewLineRendered.prototype.Measure = function();
    // Класс Paragraph
    function CParagraph()
    {
        //...................
        // Начальный сдвиг
        this.X = 0;
        this.Y = 0;
        // Ширина строки 
        this.XLimit = 0;
        // Содержимое параграфа, состоящее из объектов типа ParaText, ParaSpace и ParaNewLineRendered
        this.Content = new Array();
        // Высота строки. Пусть у нас она будет одинаковая для всех строк
        this.dLineHeight = 0;
        //...................	
    }
    CParagraph.prototype.Recalculate = function()
    {        
        // Подготовка к новому пересчету. Здесь мы удаляем старые элементы ParaNewLineRendered
        //..............................
        // Смещаемся в начало параграфа
        var X = this.X, Y = this.Y;
        // Рассчитываем параграф   
        var CurLine = 0;
        var bNewLine         = false;
        var bFirstItemOnLine = true;
        var bWord            = false;
        var nWordStartPos    = 0;
        var dWordLen         = 0;
        for ( var nPos = 0; nPos < this.Content.length; nPos++ )
        {
            var Item = this.Content[nPos];
            switch( Item.Type )
            {
                case para_Text:
                {
                    var dLetterLen = Item.Measure( Context ).Width;
                    if ( !bWord )
                    {
                        // Если слово только началось, и до него на строке ничего не было, тогда не надо проверять убирается ли оно на строке.					
                        if ( !bFirstItemOnLine )
                        {
                            if ( X + dLetterLen > this.XLimit )
                            {
                                // Уже первая буква в слове не убралась в строке - ставим перенос строки
                                this.Content.splice( nPos, 0, new ParaNewLineRendered() );
                                bNewLine = true;
                            }
                        }
                        if ( !bNewLine )
                        {
                            nWordStartPos = nPos;
                            dWordLen      = dLetterLen;
                            bWord         = true;
                        }
                    }
                    else
                    {
                        if ( X + dWordLen + dLetterLen > this.XLimit )
                        {
                            if ( bFirstItemOnLine )
                            {
                                // Слово оказалось единственным элементом строки и все равно
                                // не умещается целиком. Ставим перенос строки на текущей позиции.
                                X += dWordLen;
                                this.Content.splice( nPos, 0, new ParaNewLineRendered() );
                                bNewLine = true;
                            }
                            else
                            {
                                // Смещаемся к началу слова и перед ним ставим перенос строки
                                this.Content.splice( nWordStartPos, 0, new ParaNewLineRendered() );
                                nPos = nWordStartPos;
                                bNewLine = true;
                            }
                        }
                        if ( !bNewLine )
                        {
                            dWordLen += dLetterLen;
                        }
                    }
                    break;
                }
                case para_Space:
                {
                    bFirstItemOnLine = false;
                    var dSpaceLen = Item.Measure( Canvas ).Width;
                    if ( bWord )
                    {
                        // Не надо проверять, убирается ли слово - мы это проверяем при добавлении букв
                        X += dWordLen;
                        bWord    = false;
                        dWordLen = 0;
                    }
                    if ( X + dSpaceLen > this.XLimit )
                    {
                        bNewLine = true;
                    }
                    else
                        X += dSpaceLen;
                    break;
                }
            }
            // Переходим на новую строку
            if ( bNewLine )
            {
                bNewLine         = false;
                bFirstItemOnLine = true;
                bWord            = false;
                dWordLen         = 0;
                // По горизонтали смещаемся в начало параграфа, а по вертикали смещаемся вниз на высоту строки
                X = this.X;
                Y += this.dLineHeight;
                CurLine++;
            }
        }
    }
    CParagraph.prototype.Draw = function(Context)
    {
        var Y = this.Y;
        var X = this.X;
        var nCount = this.Content.length;
        for ( var nPos = 0; nPos < nCount; nPos++ )
        {
            var Item = this.Content[nPos];
            Item.Draw( X, Y, Context );
            X += Item.Width;
            if ( para_NewLineRendered == Item.Type )
            {
                Y += this.dLineHeight;
                X = this.X;
            }
        }
    }
    


    Ilya, lead programmer

    Two weeks after the start of development, the first test example was ready:


    You can download and test the example here .
    There is nothing nicer than comparing it with what we have achieved in a few years. And with what stage the editors of competitors are now.


    You can test all editors without registering on personal.teamlab.com . By the way, you can actively use it in general - it is free.

    Our goal? ONLYOFFICE users worldwide

    We really have a lot of plans. Already, we are actively working on mobile and desktop applications, in addition to this, the version of office editors 3.0 will be released by the fall, and we are also thinking about committing development on GitHub in real time.

    Everything seems to be. We move the discussion into comments)

    Also popular now: