diff --git a/front/css/ttb.css b/front/css/ttb.css
new file mode 100644
index 0000000000000000000000000000000000000000..1dd3d09815820f0aa4aaf64bd201959ed95e4a74
--- /dev/null
+++ b/front/css/ttb.css
@@ -0,0 +1,57 @@
+/* vertical styles: */
+
+#vertical_text_input{
+    writing-mode: vertical-lr;
+    text-orientation: upright;
+    width:auto!important;
+    height: auto;
+    white-space: nowrap!important;
+    margin: .375rem 0;
+}
+#vertical_text_input:focus{
+    outline: none;
+}
+
+#textInputBorderWrapper{
+    box-sizing: content-box;
+    padding: 0 .75rem;
+}
+
+#diplomatic-lines.ttb{
+    writing-mode: vertical-lr;
+    text-orientation: upright;  
+}
+
+#diplomatic-lines.ttb div{
+    text-align: left;  
+    min-width:25px;
+    padding: 0;
+    margin: 0;
+    padding-top: 30px;
+
+    position: relative;
+    white-space: nowrap;
+}
+
+#diplomatic-lines.ttb div::before{
+    writing-mode: horizontal-tb !important;
+    position: absolute;
+    display: block; /*to set size properties*/
+    white-space: nowrap;
+    text-align: left;  
+
+    width: 100%; /*adapt to #diplomatic-lines.ttb div size - set to position:relative*/
+    height: auto;
+    
+    margin-left: 0;
+    margin-right: .5rem;
+    margin-top: -30px;
+    margin-bottom: 20px;
+
+    padding-left: .5rem;
+    padding-right: 0;
+
+    border-bottom: 1px solid #ddd;
+    border-right: none!important;
+    border-right-width:0!important;
+}
diff --git a/front/src/main.js b/front/src/main.js
index 70a201056ccc4f1ddadd03a3b1fccaf56e94d086..6701affcac361dec89604a6ac195371a52bab37e 100644
--- a/front/src/main.js
+++ b/front/src/main.js
@@ -1,5 +1,6 @@
 import '../css/escriptorium.css';
 import '../css/rtl.css';
+import '../css/ttb.css';
 import './ajax.js';
 import { Alert, bootWebsocket, joinDocumentRoom } from './messages.js';
 window.Alert = Alert;
@@ -29,4 +30,4 @@ import { bootImageCards } from './image_cards.js';
 window.bootImageCards = bootImageCards;
 
 import { bootModels } from './models.js';
-window.bootModels = bootModels;
\ No newline at end of file
+window.bootModels = bootModels;
diff --git a/front/vue/components/DiploPanel.vue b/front/vue/components/DiploPanel.vue
index 4da1265f94f744da00cf08334cadcac945091fd8..82409240c3d97e54fa341ce7953f0d8dbfc8d93d 100644
--- a/front/vue/components/DiploPanel.vue
+++ b/front/vue/components/DiploPanel.vue
@@ -18,7 +18,9 @@
                         v-bind:key="'DL' + line.pk">
             </diploline>
 
-            <div id="diplomatic-lines"
+            <!--adding a class to get styles for ttb direction:-->
+            <div :class="$store.state.document.mainTextDirection"
+                    id="diplomatic-lines"
                     ref="diplomaticLines"
                     contenteditable="true"
                     autocomplete="off"
@@ -65,6 +67,9 @@ export default Vue.extend({
         }.bind(this), 10000);
     },
     mounted() {
+        // fix the original width so that when content texts are loaded/page refreshed with diplo panel, the panel width wont be bigger than other, especially for ttb text:
+        document.querySelector('#diplo-panel').style.width = document.querySelector('#diplo-panel').clientWidth + 'px';
+
         Vue.nextTick(function() {
             var vm = this ;
             vm.sortable = Sortable.create(this.$refs.diplomaticLines, {
diff --git a/front/vue/components/TranscriptionModal.vue b/front/vue/components/TranscriptionModal.vue
index 640ce673e574cbc89aa637747007f41d7c46f5bd..5cb968f7afa9f6580d7c78900bb27bcfc6a32970 100644
--- a/front/vue/components/TranscriptionModal.vue
+++ b/front/vue/components/TranscriptionModal.vue
@@ -70,7 +70,8 @@
                     </div>
 
                     <div id="trans-input-container" ref="transInputContainer">
-                        <input v-on:keyup.down="$store.dispatch('lines/editLine', 'next')"
+                        <input v-if="$store.state.document.mainTextDirection != 'ttb'"
+                                v-on:keyup.down="$store.dispatch('lines/editLine', 'next')"
                                 v-on:keyup.up="$store.dispatch('lines/editLine', 'previous')"
                                 v-on:keyup.enter="$store.dispatch('lines/editLine', 'next')"
                                 id="trans-input"
@@ -80,6 +81,30 @@
                                 v-model.lazy="localTranscription"
                                 autocomplete="off"
                                 autofocus/>
+                        <!--Hidden input for ttb text: -->
+                        <input v-else
+                                id="trans-input" 
+                                ref="transInput"
+                                name="content" 
+                                type="hidden"
+                                v-model.lazy="localTranscription"
+                                autocomplete="off" />
+                        <!-- in this case, input field is replaced by: -->
+                        <div v-if="$store.state.document.mainTextDirection == 'ttb'"
+                            id="textInputWrapper">
+                            <div id="textInputBorderWrapper" class="form-control mb-2">
+                                <div    v-on:blur="localTranscription = $event.target.textContent"
+                                        v-on:keyup="recomputeInputCharsScaleY()"
+                                        v-on:keyup.right="$store.dispatch('lines/editLine', 'next')"
+                                        v-on:keyup.left="$store.dispatch('lines/editLine', 'previous')"
+                                        v-on:keyup.enter="cleanHTMLTags();recomputeInputCharsScaleY();$store.dispatch('lines/editLine', 'next')"
+                                        v-html="localTranscription"
+                                        id="vertical_text_input"
+                                            contenteditable="true">
+                                </div>
+                            </div>
+                        </div>
+
                         <small v-if="line.currentTrans && line.currentTrans.version_updated_at" class="form-text text-muted">
                             <span>by {{line.currentTrans.version_author}} ({{line.currentTrans.version_source}})</span>
                             <span>on {{momentDate}}</span>
@@ -185,9 +210,27 @@ export default Vue.extend({
         $(this.$refs.transModal).draggable({handle: '.modal-header'});
         $(this.$refs.transModal).resizable();
         this.computeStyles();
+        let modele = this;
 
         let input = this.$refs.transInput;
-        input.focus();
+
+        // no need to make focus on hiden input with a ttb text
+        if(this.$store.state.document.mainTextDirection != 'ttb'){
+            input.focus();
+        }else{  // avoid some br or other html tag for a copied text on an editable input div (vertical_text_input): 
+            // 
+            document.getElementById("vertical_text_input").addEventListener("paste", function(e) {
+
+                // cancel paste to treat its content before inserting it
+                e.preventDefault();
+
+                // get text representation of clipboard
+                var text = (e.originalEvent || e).clipboardData.getData('text/plain');
+                this.innerHTML = text;
+                modele.recomputeInputCharsScaleY();
+
+            }, false);
+        }
     },
     watch: {
         line(new_, old_) {
@@ -240,7 +283,19 @@ export default Vue.extend({
         close() {
             $(this.$refs.transModal).modal('hide');
         },
-
+        cleanHTMLTags(){
+            document.getElementById("vertical_text_input").innerHTML = document.getElementById("vertical_text_input").textContent;
+        },
+        recomputeInputCharsScaleY(){
+            
+            let inputHeight = document.getElementById("vertical_text_input").clientHeight;
+            let wrapperHeight = document.getElementById("textInputBorderWrapper").clientHeight;
+            let textScaleY = wrapperHeight / (inputHeight + 10);
+
+            // to avoid input text outside the border box:
+            if(inputHeight > wrapperHeight)
+                document.getElementById("vertical_text_input").style.transform = "scaleY("+textScaleY+")";
+        },
         comparedContent(content) {
             if (!this.line.currentTrans) return;
             let diff = Diff.diffChars(this.line.currentTrans.content, content);
@@ -286,6 +341,8 @@ export default Vue.extend({
 
             // calculate rotation needed to get the line horizontal
             let target_angle = 0;  // all lines should be topologically ltr
+            if(this.$store.state.document.mainTextDirection == 'ttb') // add a 90 angle for vertical texts
+                target_angle = 90; 
             let angle = target_angle - this.getLineAngle();
 
             // apply it to the polygon and get the resulting bbox
@@ -309,8 +366,14 @@ export default Vue.extend({
 
             let context = hContext*lineHeight;
             let visuHeight = lineHeight + 2*context;
-            modalImgContainer.style.height = visuHeight+'px';
 
+            if(this.$store.state.document.mainTextDirection != 'ttb'){
+                modalImgContainer.style.height = visuHeight+'px';
+            }else{
+                modalImgContainer.style.width = visuHeight+'px';
+                modalImgContainer.style.display = 'inline-block';
+                modalImgContainer.style.verticalAlign = 'top';
+            }
             let top = -(bbox.top*ratio - context);
             let left = -(bbox.left*ratio - context);
 
@@ -353,6 +416,8 @@ export default Vue.extend({
             // Content input
             let container = this.$refs.transInputContainer;
             let input = container.querySelector('#trans-input');
+            let verticalTextInput;
+
             // note: input is not up to date yet
             let content = this.line.currentTrans && this.line.currentTrans.content || '';
             let ruler = document.createElement('span');
@@ -360,13 +425,29 @@ export default Vue.extend({
             ruler.style.visibility = 'hidden';
             ruler.textContent = content;
             ruler.style.whiteSpace="nowrap"
+
+            if(this.$store.state.document.mainTextDirection == 'ttb'){
+                // put the container inline for vertical transcription:
+                container.style.display = 'inline-block';
+                verticalTextInput = container.querySelector('#vertical_text_input');
+                // apply vertical writing style to the ruler:
+                ruler.style.writingMode = 'vertical-lr';
+                ruler.style.textOrientation = 'upright';
+            }
+
             container.appendChild(ruler);
 
             let context = hContext*lineHeight;
             let fontSize = Math.max(15, Math.round(lineHeight*0.7));  // Note could depend on the script
             ruler.style.fontSize = fontSize+'px';
-            input.style.fontSize = fontSize+'px';
-            input.style.height = Math.round(fontSize*1.1)+'px';
+
+            if(this.$store.state.document.mainTextDirection != 'ttb'){
+                input.style.fontSize = fontSize+'px';
+                input.style.height = Math.round(fontSize*1.1)+'px';
+            }else{
+                verticalTextInput.style.fontSize = fontSize+'px';
+                verticalTextInput.style.width = Math.round(fontSize*1.1)+'px';
+            }
 
             if (this.$store.state.document.readDirection == 'rtl') {
                 container.style.marginRight = context+'px';
@@ -375,15 +456,40 @@ export default Vue.extend({
                 // TODO: deal with other directions
                 container.style.marginLeft = context+'px';
             }
-            if (content) {
-                let lineWidth = bbox.width*ratio;
-                var scaleX = Math.min(5,  lineWidth / ruler.clientWidth);
-                scaleX = Math.max(0.2, scaleX);
-                input.style.transform = 'scaleX('+ scaleX +')';
-                input.style.width = 100/scaleX + '%';
-            } else {
-                input.style.transform = 'none';
-                input.style.width = '100%'; //'calc(100% - '+context+'px)';
+            if(this.$store.state.document.mainTextDirection != 'ttb'){
+                if (content) {
+                    let lineWidth = bbox.width*ratio;
+                    var scaleX = Math.min(5,  lineWidth / ruler.clientWidth);
+                    scaleX = Math.max(0.2, scaleX);
+                    input.style.transform = 'scaleX('+ scaleX +')';
+                    input.style.width = 100/scaleX + '%';
+                } else {
+                    input.style.transform = 'none';
+                    input.style.width = '100%'; //'calc(100% - '+context+'px)';
+                }
+            }else{
+                let modalImgContainer = this.$refs.modalImgContainer;
+                let textInputWrapper = container.querySelector('#textInputWrapper');
+                let textInputBorderWrapper = container.querySelector('#textInputBorderWrapper');
+                if (content) {
+                    let lineWidth = bbox.height*ratio;
+                    var scaleY = Math.min(5,  lineWidth / ruler.clientHeight);
+                    //var scaleY = Math.min(5,  lineWidth / modalImgContainer.clientHeight);
+                    //var scaleY = Math.min(5,  modalImgContainer.clientHeight / ruler.clientHeight);
+                    //var scaleY = Math.min(5,  modalImgContainer.clientHeight / textInputWrapper.clientHeight) * 0.7;
+                    scaleY = Math.max(0.2, scaleY);
+                    verticalTextInput.style.transformOrigin = 'top';
+                    verticalTextInput.style.transform = 'scaleY('+ scaleY +')';
+                    //document.getElementById('vertical_text_input').style.height = 100/scaleY + '%'; // not needed here
+                } else {
+                    verticalTextInput.style.transform = 'none';
+                    verticalTextInput.style.height = modalImgContainer.clientHeight + 'px'; 
+                }  
+                textInputWrapper.style.height = modalImgContainer.clientHeight + 'px';
+                // simulate an input field border to fix it to the actual size of the image
+                textInputBorderWrapper.style.width = verticalTextInput.clientWidth+'px';
+                //textInputBorderWrapper.style.width = verticalTextInput.offsetWidth+'px';
+                textInputBorderWrapper.style.height = modalImgContainer.clientHeight+'px';
             }
             container.removeChild(ruler);  // done its job
         },
@@ -397,13 +503,33 @@ export default Vue.extend({
 
             let bbox = this.getRotatedLineBBox();
             let hContext = 0.3; // vertical context added around the line, in percentage
-            let ratio = modalImgContainer.clientWidth / (bbox.width + (2*bbox.height*hContext));
-            let MAX_HEIGHT = Math.round(Math.max(25, (window.innerHeight-230) / 3));
-            let lineHeight = Math.max(30, Math.round(bbox.height*ratio));
-            if (lineHeight > MAX_HEIGHT) {
-                // change the ratio so that the image can not get too big
-                ratio = (MAX_HEIGHT/lineHeight)*ratio;
-                lineHeight = MAX_HEIGHT;
+
+            //
+            let ratio = 1;
+            let lineHeight = 150; 
+
+            if(this.$store.state.document.mainTextDirection != 'ttb')
+            {
+                ratio = modalImgContainer.clientWidth / (bbox.width + (2*bbox.height*hContext));
+                let MAX_HEIGHT = Math.round(Math.max(25, (window.innerHeight-230) / 3));
+                lineHeight = Math.max(30, Math.round(bbox.height*ratio));
+                if (lineHeight > MAX_HEIGHT) {
+                    // change the ratio so that the image can not get too big
+                    ratio = (MAX_HEIGHT/lineHeight)*ratio;
+                    lineHeight = MAX_HEIGHT;
+                }
+            }else{ // permutation of sizes for ttb text
+
+                modalImgContainer.style.height=String(window.innerHeight-230) + "px";   //   needed to fix height or ratio is nulled
+                ratio = modalImgContainer.clientHeight / (bbox.height + (2*bbox.width*hContext));
+                let MAX_WIDTH = 30;
+                lineHeight = Math.max(30, Math.round(bbox.width*ratio));   
+                
+                if (lineHeight > MAX_WIDTH) {    
+                    // change the ratio so that the image can not get too big
+                    ratio = (MAX_WIDTH/lineHeight)*ratio;
+                    lineHeight = MAX_WIDTH;
+                }
             }
 
             this.computeImgStyles(bbox, ratio, lineHeight, hContext);
diff --git a/front/vue/components/VisuLine.vue b/front/vue/components/VisuLine.vue
index 4e1182534f7fe52303056cda7cbcdb1d03caf8ce..8c98b1f0db3122337d0eb580bf75a6ce665aab55 100644
--- a/front/vue/components/VisuLine.vue
+++ b/front/vue/components/VisuLine.vue
@@ -10,14 +10,28 @@
                 fill="none"
                 v-bind:stroke="pathStrokeColor"
                 v-bind:d="baselinePoints"></path>
+
+        <text :text-anchor="$store.state.document.defaultTextDirection == 'rtl' ? 'end' : ''"
+                ref="textElement"
+                lengthAdjust="spacingAndGlyphs" 
+                v-if="$store.state.document.mainTextDirection != 'ttb'">
+            <textPath v-bind:href="'#' + textPathId"
+                        v-if="line.currentTrans">
+                {{ line.currentTrans.content }}
+            </textPath>
+        </text>
+
         <text :text-anchor="$store.state.document.defaultTextDirection == 'rtl' ? 'end' : ''"
                 ref="textElement"
-                lengthAdjust="spacingAndGlyphs">
+                rotate="-90"
+                font-size="1em"
+                v-else>
             <textPath v-bind:href="'#' + textPathId"
                         v-if="line.currentTrans">
                 {{ line.currentTrans.content }}
             </textPath>
         </text>
+
     </g>
 </template>
 
@@ -26,6 +40,8 @@ import { LineBase } from '../../src/editor/mixins.js';
 
 export default Vue.extend({
     mixins: [LineBase],
+    mounted() {
+    },
     watch: {
         'line.currentTrans.content': function(n, o) {
             this.$nextTick(this.reset);
@@ -41,18 +57,43 @@ export default Vue.extend({
                 let poly = this.line.mask.flat(1).map(pt => Math.round(pt));
                 var area = 0;
                 // A = 1/2(x_1y_2-x_2y_1+x_2y_3-x_3y_2+...+x_(n-1)y_n-x_ny_(n-1)+x_ny_1-x_1y_n),
-                for (let i=0; i<poly.length; i++) {
-                    let j = (i+1) % poly.length; // loop back to 1
-                    area += poly[i][0]*poly[j][1] - poly[j][0]*poly[i][1];
+
+                var liste = String(poly).split(",");
+                var indexCoordonnee = 0;
+                var arrayCoordonnees = new Array();
+                var paire = [];
+                for(var i = 0; i < liste.length; i++){
+                    paire.push(liste[i]);
+                    if(indexCoordonnee==0){
+                        indexCoordonnee = 1;
+                    }else{
+                        indexCoordonnee = 0;
+                        arrayCoordonnees.push(paire);
+                        paire = new Array();
+                    }
+                }
+
+                for (let i=0; i<arrayCoordonnees.length; i++) {
+                    let j = (i+1) % arrayCoordonnees.length; // loop back to 1
+                    area += arrayCoordonnees[i][0]*arrayCoordonnees[j][1] - arrayCoordonnees[j][0]*arrayCoordonnees[i][1];
                 }
+
                 area = Math.abs(area*this.ratio);
                 lineHeight = area / this.$refs.pathElement.getTotalLength();
+
             } else {
                 lineHeight = 30;
             }
+ 
+            lineHeight = Math.max(Math.round(lineHeight), 5) * 0.3;
+
+            let ratio = 1/4;    //  more well suited for horizontal latin writings
+            if(this.$store.state.document.mainTextDirection == 'ttb')
+                ratio = 1/2;
+
+            this.$refs.textElement.setAttribute("font-size", String(lineHeight * (ratio)) + 'px'); 
 
-            lineHeight = Math.max(Math.min(Math.round(lineHeight), 100), 5);
-            this.$refs.textElement.style.fontSize =  lineHeight * (1/2) + 'px';
+            //return lineHeight+'px';
             return 10+'px';
         },
         computeTextLength() {