Position SVG in identical position as HTML with text inside

I am trying to position a svg with text in exactly the same position as div with text, no matter the font family used or font size

The problem comes with the first element being offset in x and y position.

I am trying to set both elements in top 0 left 0 absolute position but they fail to match .

Of course manually adding correct offset x and y works, but I want a general solution.

Here is an example that if you run, the texts won’t be positioned correctly one on top of another.
I am trying to make this work with any font, any size.

<div style="position:absolute;color:red;font-family:Verdana;font-size:20px; margin-left:0; padding-left:0; left:0; ">
  First Line
</div>
<div style="position:absolute;top:0;left :0; background:transparent;"> 
<svg width="100" height="20" xmlns="http://www.w3.org/2000/svg">
<text x="0" y="0" font-family="Verdana" text-anchor="start" dominant-baseline="hanging" font-size="20" fill="black">First Line</text>
</svg>
</div>

Here is a codepen also https://codepen.io/Cristian-M/pen/VwJzXJv

Modifying the font size (make it bigger) and the offset is visible more and more

Any ideas?
Thanks!

5

I’m afraid you can only partially solve this task. Mainly due to SVG’s text-composing and layout limitations that make a perfect (responsive) replication of HTML elements quite impossible:

  • no line-wrapping in SVG (I hope I need to revise this when e.g inline-size get’s finalized in the W3C specs and adopted – I won’t hold my breath …)
  • no automatic content based bounding box adjustments (not to speak of CSS layout features like flex or grid)

In other words: to emulate/replicate HTML layouts in SVG we need to calculate suitable values via JavaScript.

Approach 1: inject SVG as inlined elements

We’re basically inheriting most of the the HTML font properties to the SVG <text> element as we’re rather injecting the SVG “lookalike” into the existing HTML/CSS layout context. Most notably we’re setting the SVG text-baseline to the bottom of the viewBox and apply overflow:visible to clipped descenders or ascenders: the SVG element will move like a HTML text element respecting the inherited font-size.

let svg = document.querySelector('.svgText')
let {width} = svg.getBBox();

// adjust viewBox
svg.setAttribute('viewBox', [0,0, width, 1].join(' '));


// fontSize change
inputFontSize.addEventListener('input', e=>{
  let fontSize = +e.currentTarget.value;
  document.body.style.fontSize = `${fontSize}px`;
})
body {
  font-family: verdana;
  font-size: 80px;
}

* {
  box-sizing: border-box;
}

.fnt-siz, .tools,
h3 {
  font-size: 16px;
  line-height: 1.2em;
}

.tools {
  position: sticky;
  top: 0;
  background: #000;
  color: #fff;
  padding: 0.5em;
  font-size: 20px;
  line-height: 1.2em;
}

.htmlText {
  position: absolute;
  background: yellow;
  color: red;
  margin-left: 0;
  padding-left: 0;
  left: 0.5em;
  top: 2em;
}

svg {
  font-size: 1px;
  letter-spacing: inherit;
}

.svgTextOverlay {
  position: relative;
}

svg {
  font-size: 1em;
  height: 1em;
  overflow: visible;
  outline: 2px dotted red;
}

.svgText {
  position: absolute;
  left: 0;
  top: 0;
}
<div class="tools">
  <p><label>fontsize <input id="inputFontSize" type="range" value="80" min="10" max="200"></label></p>
</div>

  <h3>Synchonize SVG with HTML font-size</h3>
  <div class="htmlText">
    <span class="svgTextOverlay">
      Hamburg fonts
      <!-- svg text overlay -->
      <svg id="svg1" class="svgText" viewBox="0 0 1 1"  >
        <text x="0" y="1" font-size="1" >Hamburg fonts</text>
      </svg>
    </span>
  </div>

As you can see, we can reproduce the HTML text properties quite accurately… unless we get a line break.

The most simplistic SVG text replacement placeholder could be something like this:

SVG

<svg class="svgText" viewBox="0 0 1 1">
  <text y="1" font-size="1">
    Your text
  </text>
</svg>

The required CSS to make the SVG “float” on the baseline like regular text (and also inherit properties like text color) would be:

CSS

.svgText{
  display:inline-block;
  font-size: 1em;
  height: 1em;
  overflow: visible;
  fill: currentColor;
}

JS

Required to calculate a suitable viewBox width

let {width} = svg.getBBox();
svg.setAttribute('viewBox', [0,0, width, 1].join(' '));

let svgTexts = document.querySelectorAll('.svgText');


(async() => {
  await document.fonts.ready;
  replaceHTMLText(svgTexts)
})();


function replaceHTMLText(svgTexts) {
  svgTexts.forEach(svg => {
    let {
      width
    } = svg.getBBox();
    svg.setAttribute('viewBox', [0, 0, width, 1].join(' '));
  })
}
body {
  font-size: 5vmin;
  font-family: georgia, serif;
  padding: 0.5em;
}

.svgText {
  display: inline-block;
  font-size: 1em;
  height: 1em;
  overflow: visible;
  fill: currentColor;
}

.resize {
  width: 75%;
  overflow: auto;
  padding: 0.2em;
  outline: 1px solid #ccc;
  resize: both;
}

p:hover .svgText {
  color: red
}
<h3>Hover to see the SVG text elements</h3>
<div class="resize">
  <p>One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a horrible vermin. He lay on his armour-like back, and if he lifted his head a little he could see his brown belly, <svg class="svgText" viewBox="0 0 1 1"><text y="1" font-size="1">slightly</text></svg>    domed and divided by arches into stiff sections. The bedding was <svg class="svgText" viewBox="0 0 1 1"><text y="1" font-size="1">hardly</text></svg> able to cover it and seemed ready to slide off any moment.</p>
</div>

Approach 2: Calculate y-offsets via measureText() for absolute position

We can retrieve some vertical font metrics via measureText() to calculate suitable offsets for the SVG. See also “4.12.5.1.11 Drawing text to the bitmap”

(async() => {
  await document.fonts.ready;
  positionSVGTextOverlay(htmlText, svgText);


})()

function positionSVGTextOverlay(htmlEl, svgEl) {
  // copy styles
  let styles = window.getComputedStyle(htmlEl);
  let {
    fontFamily,
    fontSize
  } = styles;
  fontSize = parseFloat(fontSize);


  //get HTML element position
  let {
    x,
    y,
    bottom,
    width,
    height
  } = htmlEl.getBoundingClientRect();

  // get vertical metrics to calculate y offset
  let metrics = getFontMetrics(fontFamily);
  let {
    fontBoundingBoxAscent,
    fontBoundingBoxDescent,
    hangingBaseline,
    alphabeticBaseline
  } = metrics;

  let renderedHeight = (fontBoundingBoxDescent + alphabeticBaseline);
  let scale = 1 + (fontBoundingBoxDescent + alphabeticBaseline) / fontBoundingBoxDescent
  let top = Math.floor(bottom - fontSize * scale)


  svgEl.setAttribute(
    "style",
    `
  position:absolute;
  display:inline-block;
  font-size: ${fontSize}px;
  font-family: ${fontFamily};
  left: ${x}px;
  top: ${ top  }px;
  `
  );

  //adjust viewBox
  let bb = svgEl.getBBox();
  svgEl.setAttribute('viewBox', [0, 0, bb.width, 1].join(' '));


}



function getFontMetrics(font) {
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");
  ctx.font = `1000px ${font}`;
  ctx.textBaseline = "top";
  return ctx.measureText('H');
}
html,
body {
  margin: 0;
  padding: 0;
}

.fnt-siz,
.tools,
h3 {
  font-size: 16px;
  line-height: 1.2em;
}

.tools {
  position: sticky;
  top: 0;
  background: #000;
  color: #fff;
  padding: 0.5em;
  font-size: 20px;
  line-height: 2.5em;
}

.htmlText {
  font-family: sans-serif, Verdana, Georgia;
  position: absolute;
  background: yellow;
  color: red;
  margin-left: 0;
  padding-left: 0;
  left: 50px;
  top: 200px;
  font-size: 90px;
}

.svgText {
  font-size: 1em;
  height: 1em;
  overflow: visible;
  outline: 1px solid red;
}
<div class="tools">
  <p><label>fontsize <input id="inputFontSize" type="range" value="80" min="10" max="200"></label></p>
</div>

<h3>Synchonize SVG with HTML font-size</h3>
<div id="htmlText" class="htmlText">
  First Line
</div>

<!-- svg text overlay -->
<svg id="svgText" class="svgText" viewBox="0 0 1 1">
  <text y="1" font-size="1" >First Line
</text>
</svg>

<script>
  // fontSize change
  inputFontSize.addEventListener("input", (e) => {
    let fontSize = +e.currentTarget.value;
    htmlText.style.fontSize = `${fontSize}px`;
    positionSVGTextOverlay(htmlText, svgText);

  });
</script>

However, we still can’t emulate a line breaks.

1

Doing tests, it seems I found the solution.
If you want to have the SVG <text identical to dom <div text, what you need to do is set:

dominant-baseline=”center” (not middle, not hanging or any other).

Example:

<svg  width="300" height="300" xmlns="http://www.w3.org/2000/svg">
<text x="0" y="40px" font-family="verdana" text-anchor="start" font-size="80px" dominant-baseline="central"   fill="black">First Line</text>
  <text x="0" y="120px" font-family="verdana" text-anchor="start" font-size="80px" dominant-baseline="central"   fill="black">Second Line</text>
</svg>

And you need to set the text Y position, to be half the font size

here is a code pen showing the easy solution:
https://codepen.io/Cristian-M/pen/VwJzxmK

@blaster god i think you will need to put both elements inside a wrapper. Set the wrapper to relative positioning. Then, set both elements to absolute positioning with top and left set to 0. Also, make sure the div’s line height is the same as its font size. Use the same font size and font family for both element and remove that extra a

First Line

First Line

New contributor

Goody Programmer Boy is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

2

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật