Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 151 additions & 64 deletions src/main/js/html.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
* POSSIBILITY OF SUCH DAMAGE.
*/

import { reportError } from "./error.js";
import { reportError, reportWarning } from "./error.js";
import { byName } from "./styles.js";

/**
Expand Down Expand Up @@ -211,15 +211,6 @@ function processElement(context, dom_parent, isd_element, isd_parent) {

e = document.createElement("rt");

} else if (isd_element.styleAttrs[byName.ruby.qname] === "baseContainer") {

e = document.createElement("span"); // rbc element is deprecated in HTML

} else if (isd_element.styleAttrs[byName.ruby.qname] === "textContainer") {

e = document.createElement("span"); // rtc element is deprecated in HTML
e.style.display = "ruby-text-container";

} else if (isd_element.styleAttrs[byName.ruby.qname] === "delimiter") {

/* ignore rp */
Expand Down Expand Up @@ -455,9 +446,19 @@ function processElement(context, dom_parent, isd_element, isd_parent) {

if ("contents" in isd_element) {

for (let k = 0; k < isd_element.contents.length; k++) {
if (isd_element.kind === "span" &&
isd_element.styleAttrs[byName.ruby.qname] === "container" &&
isd_element.contents.some((c) => c.styleAttrs[byName.ruby.qname] === "baseContainer")) {

processRubyContainer(context, e, isd_element);

} else {

for (let k = 0; k < isd_element.contents.length; k++) {

processElement(context, proc_e, isd_element.contents[k], isd_element);

processElement(context, proc_e, isd_element.contents[k], isd_element);
}

}

Expand Down Expand Up @@ -583,6 +584,104 @@ function processElement(context, dom_parent, isd_element, isd_parent) {
}
}

/*
* Maps a TTML ruby container that contains a ruby base container to N <ruby>
* elements, one for each ruby base of the base container, since <rtc> and <rbc>
* are deprecated.
*
* The ith <ruby> element contains:
* - the ith ruby base, or, if there are two ruby text containers, a nested
* <ruby> element that contains the ith ruby base and the ith ruby text of the
* second text container; followed by
* - the ith ruby text of the first text container.
*
* If a text container has more ruby texts than the base container has ruby
* bases, the extra ruby texts are kept and annotate an empty base.
*/
function processRubyContainer(context, ruby, isd_ruby) {

let bases = [];

const textContainers = [];

for (const c of isd_ruby.contents) {

if (c.styleAttrs[byName.ruby.qname] === "baseContainer") {

bases = c.contents.filter((b) => b.styleAttrs[byName.ruby.qname] === "base");

} else if (c.styleAttrs[byName.ruby.qname] === "textContainer") {

textContainers.push(c.contents.filter((t) => t.styleAttrs[byName.ruby.qname] === "text"));

}

}

/* number of <ruby> elements, i.e. the greater of the number of ruby bases and the number of ruby text in the first two text containers */

let ruby_count = bases.length;

for (let k = 0; k < textContainers.length && k < 2; k++) {

ruby_count = Math.max(ruby_count, textContainers[k].length);

}

if (ruby_count > bases.length) {

reportWarning(context.errorHandler, "Ruby text container has more ruby text than the ruby base container has ruby bases.");

}

const template = ruby.cloneNode(false);

let outer = ruby;

for (let i = 0; i < ruby_count; i++) {

if (i > 0) {

const next = template.cloneNode(false);

outer.parentElement.insertBefore(next, outer.nextSibling);

outer = next;

}

let base_parent = outer;

if (textContainers.length > 1) {

base_parent = template.cloneNode(false);

outer.appendChild(base_parent);

}

if (i < bases.length) {

processElement(context, base_parent, bases[i], isd_ruby);

}

if (textContainers.length > 1 && i < textContainers[1].length) {

processElement(context, base_parent, textContainers[1][i], isd_ruby);

}

if (textContainers.length > 0 && i < textContainers[0].length) {

processElement(context, outer, textContainers[0][i], isd_ruby);

}

}

}

function mergeSpans(lineList, context) {

for (let i = 0; i < lineList.length; i++) {
Expand Down Expand Up @@ -886,88 +985,76 @@ function applyRubyReserve(lineList, context) {

for (let i = 0; i < lineList.length; i++) {

const ruby = document.createElement("ruby");

const rb = document.createElement("span"); // rb element is deprecated in HTML
rb.textContent = "\u200B";

ruby.appendChild(rb);
const fs = context.rubyReserve[1].toUsedLength(context.w, context.h) + "px";

let rt1;
let rt2;
/* We cannot use the deprecated <rtc> to reserve space, so instead we use nested rubies */

const fs = context.rubyReserve[1].toUsedLength(context.w, context.h) + "px";
const reserved_pos = [];

if (context.rubyReserve[0] === "both" || (context.rubyReserve[0] === "outside" && lineList.length == 1)) {

rt1 = document.createElement("span"); // rtc element is deprecated in HTML
rt1.style.display = "ruby-text-container";
rt1.style[RUBYPOSITION_PROP] = RUBYPOSITION_ISWK ? "after" : "under";
rt1.textContent = "\u200B";
rt1.style.fontSize = fs;
reserved_pos.push(RUBYPOSITION_ISWK ? "after" : "under");
reserved_pos.push(RUBYPOSITION_ISWK ? "before" : "over");

rt2 = document.createElement("span"); // rtc element is deprecated in HTML
rt2.style.display = "ruby-text-container";
rt2.style[RUBYPOSITION_PROP] = RUBYPOSITION_ISWK ? "before" : "over";
rt2.textContent = "\u200B";
rt2.style.fontSize = fs;
} else if (context.rubyReserve[0] === "after" || (context.rubyReserve[0] === "outside" && i > 0)) {

ruby.appendChild(rt1);
ruby.appendChild(rt2);
reserved_pos.push(RUBYPOSITION_ISWK ? "after" : ((context.bpd === "tb" || context.bpd === "rl") ? "under" : "over"));

} else {

rt1 = document.createElement("span"); // rtc element is deprecated in HTML
rt1.style.display = "ruby-text-container";
rt1.textContent = "\u200B";
rt1.style.fontSize = fs;
reserved_pos.push(RUBYPOSITION_ISWK ? "before" : ((context.bpd === "tb" || context.bpd === "rl") ? "over" : "under"));

let pos;
}

if (context.rubyReserve[0] === "after" || (context.rubyReserve[0] === "outside" && i > 0)) {
/* add in front of the first ruby element of the line, if it exists */

pos = RUBYPOSITION_ISWK ? "after" : ((context.bpd === "tb" || context.bpd === "rl") ? "under" : "over");
let ruby_sibling = null;

} else {

pos = RUBYPOSITION_ISWK ? "before" : ((context.bpd === "tb" || context.bpd === "rl") ? "over" : "under");
for (let j = 0; j < lineList[i].rbc.length; j++) {

}
if (lineList[i].rbc[j].localName === "ruby") {

rt1.style[RUBYPOSITION_PROP] = pos;
ruby_sibling = lineList[i].rbc[j];

ruby.appendChild(rt1);
break;
}

}

/* add in front of the first ruby element of the line, if it exists */
let base = document.createElement("span"); // rb element is deprecated in HTML
base.textContent = "\u200B";

let sib = null;
for (const pos of reserved_pos) {

for (let j = 0; j < lineList[i].rbc.length; j++) {
const ruby = document.createElement("ruby");

if (lineList[i].rbc[j].localName === "ruby") {
ruby.appendChild(base);

sib = lineList[i].rbc[j];
const rt = document.createElement("rt");
rt.style[RUBYPOSITION_PROP] = pos;
rt.textContent = "\u200B";
rt.style.fontSize = fs;

/* copy specified style properties from the sibling ruby container */
ruby.appendChild(rt);

for (let k = 0; k < sib.style.length; k++) {
/* copy specified style properties from the sibling ruby container */

ruby.style.setProperty(sib.style.item(k), sib.style.getPropertyValue(sib.style.item(k)));
if (ruby_sibling !== null) {

for (let k = 0; k < ruby_sibling.style.length; k++) {
ruby.style.setProperty(ruby_sibling.style.item(k), ruby_sibling.style.getPropertyValue(ruby_sibling.style.item(k)));
}

break;
}

}
ruby.style[RUBYPOSITION_PROP] = pos; // for Chrome

/* otherwise add before first span */

sib = sib || lineList[i].elements[0].node;
base = ruby;
}

sib.parentElement.insertBefore(ruby, sib);
/* add before first span if there is no ruby sibling */
const actual_sibling = ruby_sibling || lineList[i].elements[0].node;
actual_sibling.parentElement.insertBefore(base, actual_sibling);

}

Expand Down Expand Up @@ -1113,7 +1200,7 @@ function rect2edges(rect, context) {

function constructLineList(context, element, llist, bgcolor) {

if (element.localName === "rt" || element.style.display === "ruby-text-container") {
if (element.localName === "rt") {

/* skip ruby annotations */

Expand Down Expand Up @@ -1205,9 +1292,9 @@ function constructLineList(context, element, llist, bgcolor) {

constructLineList(context, child, llist, curbgcolor);

if (child.localName === "ruby" || child.style.display === "ruby-text-container") {
if (child.localName === "ruby") {

/* remember non-empty ruby and rtc elements so that tts:rubyPosition can be applied */
/* remember non-empty ruby elements so that tts:rubyPosition can be applied */

if (llist.length > 0) {

Expand Down Expand Up @@ -1640,7 +1727,7 @@ const STYLING_MAP_DEFS = [

}

/* apply position to the parent dom_element, i.e. ruby or rtc */
/* apply position to the parent dom_element, i.e. ruby */

dom_element.parentElement.style[RUBYPOSITION_PROP] = pos;
}
Expand Down
Loading