ONLYOFFICE. Голая правда об исходном коде облачного офиса
В начале июля Teamlab переименовался в ONLYOFFICE и полностью открыл исходный код своего приложения, включая онлайн редакторы документов, совместное редактирование, диаграмму Ганта и функционал выставления счетов.За две недели мы получили массу вопросов на эту тему, поэтому на правах сотрудника компании возьмусь разъяснить, что к чему. Если у вас нет времени читать пост, просто скачайте инсталляцию ONLYOFFICE с Sourceforge. Разверните на своем сервере. Готово! У вас есть свой облачный офис. Вы великолепны.Почему Teamlab стал ONLYOFFICE Идея ребрендинга существовала довольно давно. В своем блоге мы написали целый пост о том, почему мы выбрали ONLY в качестве новой приставки к своему офису, однако, были и вполне конкретные причины. Во-первых, название Teamlab перестало отражать идею продукта, ведь «team» ассоциируется с небольшой командой, в то время как среди наших клиентов все чаще появляются компании по 300 и 400 человек. Во-вторых, помимо нас, к сожалению, это слово облюбовали дизайнеры интерактивных вешалок из Японии.Почему мы открыли исходный код (Honesty is the best policy потому что) Основная причина — это, безусловно, доверие. Не только со стороны клиентов, но и со стороны наших партнеров. В сегодняшней ситуации мы получаем довольно много вопросов о безопасности хранения корпоративных данных, особенно от зарубежных клиентов. Публикуя код ONLYOFFICE на Sourceforge и GitHub, мы делаем еще один шаг им навстречу.Выбор лицензии Публикуя исходные коды своего продукта, мы преследовали несколько целей: Привлечь внимание opensource сообщества. Закрепить авторство продукта. Усилить доверие зарубежных клиентов и партнеров. Взвесив все за и против, мы выбрали AGPL v3. В двух словах она означает следующее: вы можете использовать ONLYOFFICE в своей компании бесплатно и без ограничений, но если хотите встроить наш код в собственное приложение, вам придется открыть исходные коды своего продукта под той же лицензией. Подробнее здесь.Что говорят наши программисты Переходим к самой интересной части нашего поста. Open Source версия ONLYOFFICE готовилась к выпуску довольно долго — один только текстовый редактор содержит более 500 000 строк кода! Конечно же, нам есть, о чем рассказать: Особое внимание к Internet Explorer Основа алгоритма растеризации взята из freetype, так как это качественный, проверенный временем открытый шрифтовый движок. Сами алгоритмы немного изменили, чтобы скорость выполнения была выше именно в браузере, поскольку мы пишем на javascript.
А вот, например, кэш букв нам пришлось писать в двух вариантах: для Internet Explorer и для всех остальных. Из-за специфика браузера в IE первая реализация была медленной.
Олег, ведущий программистЧто вы знаете о ширине столбцов в Excel? Существует два основных сценария использования Excel:
финансовые расчёты, создание бланков (счетов) для дальнейшего заполнения и распечатки. Большое количество бланков были созданы еще до появления нашего редактора таблиц, поэтому нашей задачей было не только научиться правильно их открывать, но и корректно рассчитывать ширины столбцов и высоты строк. В противном случае при печати бланк мог бы не убраться на лист.Например, стандартная ширина столбца в Microsoft Excel — 8.43 символа (characters). Она рассчитывается из ширины по умолчанию в 8 символов, которая переводится в пиксели в зависимости от стиля Normal. Затем, как оказалось, полученное число округляется до ближайшего кратного 8-ми.Еще одна особенность — это высота строки. В Excel нельзя задать межстрочный интервал, как, например, в Word, поэтому расчет высоты строки в Excel отличается от расчета высоты строки в Word. Вот, например, сравнение в шрифте Cambria Math 11: В итоге мы реализовали расчёт так, чтобы ни один символ не оказался обрезанным. Александр, ведущий программистКак мы связали дерево зависимостей и топологическую сортировку Большая часть электронных таблиц так или иначе содержит в себе формулы. Для решения задач разбора и подсчета формул было реализованы парсер и калькулятор. Также в работе с формулами особого внимания заслуживает построение зависимостей между ячейками. Для этой цели был использован алгоритм топологической сортировки, позволяющий строить порядок обхода дерева зависимостей.
Дмитрий, программистПятнадцать тысяч тестов и 80 машин Чтобы протестировать инсталляцию ONLYOFFICE, мы развернули её у себя и запустили уже привычный процесс. В сумме набежало порядка 15000 тестов, которые запускаются на 80 машинах. Кстати, хостим мы их теперь на Digital Ocean и в ближайшее время расскажем о том, как перенесли туда всю тестовую структуру.
Стас, руководитель отдела тестированияHopes&Fears Боимся ли мы того, что наш код кто-то позаимствует в корыстных целях? Нет. Использование кода ONLYOFFICE четко прописано в лицензии, и здесь мы защищены международным правом.Не думаем ли мы, что кто-то «подсмотрит» и сделает аналогичный продукт? Конечно, думаем. Но на это у ребят уйдет не менее 3 лет, так что мы не вот уж сильно переживаем. Впрочем, мы давно уже привыкли находиться и выживать в высококонкурентной среде.
Александра, руководитель отдела продвиженияЧто вам стоит свой редактор на канве построить? Любой текстовый редактор начинается с разбивки текста на строки и отрисовки. Первый рабочий вариант состоял из 150 строк кода и был реализован следующим образом: // Класс 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); Еще 140 строк кода // Класс 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; } } } Илья, ведущий программист
Через две недели после начала разработки был готов первый тестовый пример:
Скачать и протестировать пример можно здесь.Нет ничего приятнее, чем сравнивать его с тем, чего мы достигли за несколько лет. И с тем, на каком этапе находятся сейчас редакторы конкурентов.
Протестировать все редакторы можно без регистрации на сайте personal.teamlab.com. Им, кстати, вообще можно активно пользоваться — он бесплатный.
Наша цель? Пользователи ONLYOFFICE по всему миру Планов у нас действительно много. Уже сейчас мы активно работаем над мобильными и десктопными приложениями, помимо этого к осени выйдет версия офисных редакторов 3.0, а еще мы подумываем над тем, чтобы коммитить разработку на GitHub в режиме реального времени.Кажется, все. Перемещаем обсуждение в комментарии)