Как подогнать размер шрифта чтобы текст располагался по всей длине пути

Вопрос инициирован комментарием @Leonid'a под ответом на вопрос Получить svg с изогнутым текстом в виде арки

чтобы размер шрифта подгонялся под длину пути так, чтобы текст располагался по всей длине этого пути.

.container {
 width:50vw;
 height:50vh;
 }
 #txt {
 fill:#005E46;
 font-size:52px;
 font-face:sans-serif;
 font-weight:bold;
}
<div class="container">
<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink"
       viewBox="0 10 656 544"  >  

   <path id="top" fill="none" stroke="red" d="m 86.959108,536.92194 c 0,0 -10.133315,-158.67922 2.022305,-236.60967 4.731862,-30.33627 12.118517,-61.28378 27.301117,-87.97026 15.93166,-28.00312 39.52587,-51.70293 64.71375,-71.79182 23.23934,-18.53481 49.18226,-35.65231 77.85874,-43.479558 41.30634,-11.274567 86.74257,-12.863251 128.41636,-3.033457 31.55069,7.442015 59.98355,26.093615 85.94795,45.501855 16.60888,12.41505 30.85349,28.04167 43.47955,44.49071 14.07865,18.34144 27.2555,38.01449 35.39034,59.65799 8.44276,22.46274 10.89155,46.88973 13.14498,70.78067 7.08982,75.16651 1.01115,226.49814 1.01115,226.49814" />

<text id="txt" dy="-10"  >
  <textPath  xlink:href="#top" lengthAdjust="spacingAndGlyphs" textLength="1175"    startOffset="0%" >
   Весь очень длинный текст целиком вокруг арки   
  </textPath> 
 </text> 
</svg>  
</div> 


Ответы (2 шт):

Автор решения: Alexandr_TT

Решить эту задачу помогут атрибуты text: textLength и lengthAdjust

Если установить textLength равным длине path вдоль которого расположено слово или фраза из нескольких слов, то текст займет всю длину path, при любом размере шрифта.

В примере ниже длина path арки равна 1175px устанавливаем textLength="1175" и текст полностью займет всю арку при любом размере шрифта

font-size:52px;

.container {
 width:50vw;
 height:50vh;
 }
 #txt {
 fill:#005E46;
 font-size:52px;
 font-face:sans-serif;
 font-weight:bold;
}
<div class="container">
<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink"
       viewBox="0 10 656 544"  >  

   <path id="top" fill="none" stroke="red" d="M87 537s-10.2-158.8 2-236.7c4.7-30.3 12.1-61.3 27.3-88a258 258 0 0 1 64.7-71.7c23.2-18.6 49.2-35.7 77.9-43.5a264.5 264.5 0 0 1 128.4-3c31.5 7.4 60 26 86 45.4 16.5 12.5 30.8 28 43.4 44.5 14 18.4 27.3 38 35.4 59.7 8.4 22.5 10.9 46.9 13.1 70.8 7.1 75.1 1 226.5 1 226.5" />

<text id="txt" dy="-10"  >
  <textPath  xlink:href="#top" lengthAdjust="spacingAndGlyphs" textLength="1175"    startOffset="0%" >
   Весь очень длинный текст целиком вокруг арки   
  </textPath> 
 </text> 
</svg>  
</div>  
<script>
const length = document.getElementById('top').getTotalLength();
console.log(`Длина линии:` + length);
  </script>

В следующем примере уменьшим вдвое длину textLength="1175 / 2 = 587.5"

Фраза займет ровно половину длины арки

.container {
 width:50vw;
 height:50vh;
 }
 #txt {
 fill:#005E46;
 font-size:52px;
 font-face:sans-serif;
 font-weight:bold;
}
<div class="container">
<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink"
       viewBox="0 10 656 544"  >  

   <path id="top" fill="none" stroke="red" d="M87 537s-10.2-158.8 2-236.7c4.7-30.3 12.1-61.3 27.3-88a258 258 0 0 1 64.7-71.7c23.2-18.6 49.2-35.7 77.9-43.5a264.5 264.5 0 0 1 128.4-3c31.5 7.4 60 26 86 45.4 16.5 12.5 30.8 28 43.4 44.5 14 18.4 27.3 38 35.4 59.7 8.4 22.5 10.9 46.9 13.1 70.8 7.1 75.1 1 226.5 1 226.5" />

<text id="txt" dy="-10"  >
  <textPath  xlink:href="#top" lengthAdjust="spacingAndGlyphs" textLength="587.5"    startOffset="0%" >
   Весь очень длинный текст наполовину арки 
  </textPath> 
 </text> 
</svg>  
</div>  

Удваиваем количество слов, чтобы уместиться на этой же длине textLength="1175" каждый символ сжимается.

.container {
 width:50vw;
 height:50vh;
 }
 #txt {
 fill:#005E46;
 font-size:52px;
 font-face:sans-serif;
 font-weight:bold;
}
<div class="container">
<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink"
       viewBox="0 10 656 544"  >  

   <path id="top" fill="none" stroke="red" d="M87 537s-10.2-158.8 2-236.7c4.7-30.3 12.1-61.3 27.3-88a258 258 0 0 1 64.7-71.7c23.2-18.6 49.2-35.7 77.9-43.5a264.5 264.5 0 0 1 128.4-3c31.5 7.4 60 26 86 45.4 16.5 12.5 30.8 28 43.4 44.5 14 18.4 27.3 38 35.4 59.7 8.4 22.5 10.9 46.9 13.1 70.8 7.1 75.1 1 226.5 1 226.5" />

<text id="txt" dy="-10"  >
  <textPath  xlink:href="#top" lengthAdjust="spacingAndGlyphs" textLength="1175"    startOffset="0%" >
   Весь очень длинный текст целиком вокруг арки  *** Весь очень длинный текст целиком вокруг арки   
  </textPath> 
 </text> 
</svg>  
</div>  

Тот же пример с длинной, удвоенной фразой, но размер шрифта увеличим с 52 до 72px. Фраза будет занимать тоже место по длине, но символы ещё больше сожмутся и вырастут по высоте.

.container {
 width:50vw;
 height:50vh;
 }
 #txt {
 fill:#005E46;
 font-size:72px;
 font-face:sans-serif;
 font-weight:bold;
}
    
<div class="container">
<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink"
       viewBox="0 10 656 544"  >  

   <path id="top" fill="none" stroke="red" d="M87 537s-10.2-158.8 2-236.7c4.7-30.3 12.1-61.3 27.3-88a258 258 0 0 1 64.7-71.7c23.2-18.6 49.2-35.7 77.9-43.5a264.5 264.5 0 0 1 128.4-3c31.5 7.4 60 26 86 45.4 16.5 12.5 30.8 28 43.4 44.5 14 18.4 27.3 38 35.4 59.7 8.4 22.5 10.9 46.9 13.1 70.8 7.1 75.1 1 226.5 1 226.5" />

<text id="txt" dy="-10"  >
  <textPath  xlink:href="#top" lengthAdjust="spacingAndGlyphs" textLength="1175"    startOffset="0%" >
   Весь очень длинный текст целиком вокруг арки  *** Весь очень длинный текст целиком вокруг арки   
  </textPath> 
 </text> 
</svg>  
</div>  

Вывод

Размер глифов шрифта под определенную длину с целью её полного заполнения, вычисляется самим препроцессором SVG.

Это может быть реализовано двумя способами:

  1. При большой длине textLength и lengthAdjust="spacingAndGlyphs" и малом количестве символов они будут сильно растянуты, чтобы заполнить всю длину

.container {
 width:50vw;
 height:50vh;
 }
 #txt {
 fill:#005E46;
 font-size:24px;
 font-face:sans-serif;
 font-weight:bold;
}
<div class="container">
<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink"
       viewBox="0 10 656 544"  >  

   <path id="top" fill="none" stroke="red" d="M87 537s-10.2-158.8 2-236.7c4.7-30.3 12.1-61.3 27.3-88a258 258 0 0 1 64.7-71.7c23.2-18.6 49.2-35.7 77.9-43.5a264.5 264.5 0 0 1 128.4-3c31.5 7.4 60 26 86 45.4 16.5 12.5 30.8 28 43.4 44.5 14 18.4 27.3 38 35.4 59.7 8.4 22.5 10.9 46.9 13.1 70.8 7.1 75.1 1 226.5 1 226.5" />

<text id="txt" dy="-10"  >
  <textPath  xlink:href="#top" lengthAdjust="spacingAndGlyphs" textLength="1175"    startOffset="0%" >
 ВОКРУГ АРКИ     
  </textPath> 
 </text> 
</svg>  
</div>  

  1. При большой длине textLength и lengthAdjust="spacing" (значение по умолчанию) и малом количестве символов расстояние между символами будет значительно увеличено

.container {
 width:50vw;
 height:50vh;
 }
 #txt {
 fill:#005E46;
 font-size:24px;
 font-face:sans-serif;
 font-weight:bold;
}
<div class="container">
<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink"
       viewBox="0 10 656 544"  >  

   <path id="top" fill="none" stroke="red" d="M87 537s-10.2-158.8 2-236.7c4.7-30.3 12.1-61.3 27.3-88a258 258 0 0 1 64.7-71.7c23.2-18.6 49.2-35.7 77.9-43.5a264.5 264.5 0 0 1 128.4-3c31.5 7.4 60 26 86 45.4 16.5 12.5 30.8 28 43.4 44.5 14 18.4 27.3 38 35.4 59.7 8.4 22.5 10.9 46.9 13.1 70.8 7.1 75.1 1 226.5 1 226.5" />

<text id="txt" dy="-10"  >
  <textPath  xlink:href="#top"  textLength="1175" lengthAdjust="spacing"   startOffset="0%" >
 ВОКРУГ АРКИ     
  </textPath> 
 </text> 
</svg>  
</div>  

→ Ссылка
Автор решения: Leonid

const svg = document.querySelector('svg');
const path = document.querySelector('path');
const txt = document.getElementById('txt');

txt.addEventListener('input', drawText);

drawText();

function drawText(){
    let text = txt.value; // Значение инпута - наш текст
    let real_width = svg.getBoundingClientRect().width; // Ширина SVG реальная
    let view_width = +svg.getAttribute('viewBox').split(' ')[2]; // Ширина по viewBox
    let k = real_width/view_width; // Коэффициент для корректировки размеров
    let path_len = path.getTotalLength()*k; // Реальная длина пути

    let svg_hidden = document.createElement('svg'); // Создаем другой SVG для вставки текста размером шрифта 1px для нахождения длины текста
    let text_hidden = document.createElement('text');
    text_hidden.style.fontSize = '1px';
    text_hidden.style.fontFamily = 'sans-serif';
    text_hidden.style.opacity = '0';
    text_hidden.textContent = text;
    svg_hidden.append(text_hidden);
    document.body.append(svg_hidden);

    // устанавливаем тексту из основного SVG размер, исходя из соотношения длины пути и размера текста при 1px, округляем в меньшую сторону
    svg.querySelector('text').style.fontSize = Math.floor(path_len/text_hidden.offsetWidth)/k + 'px';

    let text_path = svg.querySelector('textPath');
    text_path.textContent = text;
    text_path.setAttribute('textLength', path_len/k);

    document.body.removeChild(svg_hidden); // Удаляем вспомогательный SVG
}
<svg viewBox="0 0 600 400" height="calc(100vh - 45px)">
    <path id="textpath" d="M 50 390 L 50 290 A 250 250 180 1 1 550 290 L 550 390" fill="none" stroke="blue"></path>
    <text style="font-family:sans-serif">
        <textPath xlink:href="#textpath" lengthAdjust="spacing">        
        </textPath>
    </text>
</svg>
<input type="text" id="txt" style="width:100%" value="Как подобрать размер шрифта">

→ Ссылка