diff --git a/inception/inception-external-editor/src/main/java/de/tudarmstadt/ukp/inception/externaleditor/xhtml/XHtmlXmlDocumentViewControllerImpl.java b/inception/inception-external-editor/src/main/java/de/tudarmstadt/ukp/inception/externaleditor/xhtml/XHtmlXmlDocumentViewControllerImpl.java index 477b0d603fb..e06dbf1d75b 100644 --- a/inception/inception-external-editor/src/main/java/de/tudarmstadt/ukp/inception/externaleditor/xhtml/XHtmlXmlDocumentViewControllerImpl.java +++ b/inception/inception-external-editor/src/main/java/de/tudarmstadt/ukp/inception/externaleditor/xhtml/XHtmlXmlDocumentViewControllerImpl.java @@ -19,6 +19,7 @@ import static java.lang.invoke.MethodHandles.lookup; import static java.util.Optional.ofNullable; +import static javax.xml.XMLConstants.DEFAULT_NS_PREFIX; import static org.slf4j.LoggerFactory.getLogger; import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.MediaType.IMAGE_GIF; @@ -26,14 +27,11 @@ import static org.springframework.http.MediaType.IMAGE_PNG; import java.io.FileNotFoundException; -import java.io.IOException; import java.io.StringWriter; import java.security.Principal; import java.util.Locale; import java.util.Optional; -import javax.xml.XMLConstants; - import org.apache.commons.lang3.StringUtils; import org.apache.uima.cas.CAS; import org.dkpro.core.api.xml.type.XmlDocument; @@ -64,7 +62,6 @@ import de.tudarmstadt.ukp.inception.externaleditor.policy.DefaultHtmlDocumentPolicy; import de.tudarmstadt.ukp.inception.externaleditor.policy.SafetyNetDocumentPolicy; import de.tudarmstadt.ukp.inception.externaleditor.xml.XmlCas2SaxEvents; -import de.tudarmstadt.ukp.inception.io.xml.dkprocore.Cas2SaxEvents; import de.tudarmstadt.ukp.inception.support.wicket.ServletContextUtils; import jakarta.servlet.ServletContext; @@ -87,6 +84,7 @@ public class XHtmlXmlDocumentViewControllerImpl private static final String BODY = "body"; private static final String HEAD = "head"; private static final String P = "p"; + private static final String SPAN = "span"; private final DocumentService documentService; private final DocumentStorageService documentStorageService; @@ -160,12 +158,8 @@ public ResponseEntity getDocument(@PathVariable("projectId") long aProje // If the CAS contains an actual HTML structure, then we send that. Mind that we do // not inject format-specific CSS then! if (casContainsHtml) { - var xml = maybeXmlDocument.get(); startXHtmlDocument(rawHandler); - - var serializer = new XmlCas2SaxEvents(xml, finalHandler); - serializer.process(xml.getRoot()); - + renderXmlContent(finalHandler, maybeXmlDocument.get()); endXHtmlDocument(rawHandler); return toResponse(out); } @@ -173,31 +167,35 @@ public ResponseEntity getDocument(@PathVariable("projectId") long aProje startXHtmlDocument(rawHandler); rawHandler.startElement(null, null, HTML, null); - renderHead(doc, rawHandler); - rawHandler.startElement(null, null, BODY, null); if (maybeXmlDocument.isEmpty()) { // Gracefully handle the case that the CAS does not contain any XML structure at all // and show only the document text in this case. + var atts = new AttributesImpl(); + atts.addAttribute("", "", "class", "CDATA", "i7n-plain-text-document"); + rawHandler.startElement(null, null, BODY, atts); renderTextContent(cas, finalHandler); + rawHandler.endElement(null, null, BODY); } else { + rawHandler.startElement(null, null, BODY, null); + var formatPolicy = formatRegistry.getFormatPolicy(doc); var defaultNamespace = formatPolicy.flatMap(policy -> policy.getDefaultNamespace()); if (defaultNamespace.isPresent()) { - finalHandler.startPrefixMapping(XMLConstants.DEFAULT_NS_PREFIX, - defaultNamespace.get()); + finalHandler.startPrefixMapping(DEFAULT_NS_PREFIX, defaultNamespace.get()); } - renderXmlContent(doc, finalHandler, aEditor, maybeXmlDocument.get()); + renderXmlContent(finalHandler, maybeXmlDocument.get()); if (defaultNamespace.isPresent()) { - finalHandler.endPrefixMapping(XMLConstants.DEFAULT_NS_PREFIX); + finalHandler.endPrefixMapping(DEFAULT_NS_PREFIX); } + + rawHandler.endElement(null, null, BODY); } - rawHandler.endElement(null, null, BODY); rawHandler.endElement(null, null, HTML); @@ -229,27 +227,34 @@ private void renderHead(SourceDocument doc, ContentHandler ch) throws SAXExcepti ch.endElement(null, null, HEAD); } - private void renderXmlContent(SourceDocument doc, ContentHandler ch, Optional aEditor, - XmlDocument aXmlDocument) - throws IOException, SAXException + private void renderXmlContent(ContentHandler ch, XmlDocument aXmlDocument) throws SAXException { - Cas2SaxEvents serializer = new XmlCas2SaxEvents(aXmlDocument, ch); + var serializer = new XmlCas2SaxEvents(aXmlDocument, ch); serializer.process(aXmlDocument.getRoot()); } private void renderTextContent(CAS cas, ContentHandler ch) throws SAXException { + var lineAttribs = new AttributesImpl(); + lineAttribs.addAttribute("", "", "class", "CDATA", "data-i7n-tracking"); + var text = cas.getDocumentText().toCharArray(); ch.startElement(null, null, P, null); + ch.startElement(null, null, SPAN, lineAttribs); + var lineBreakSequenceLength = 0; for (int i = 0; i < text.length; i++) { if (text[i] == '\n') { lineBreakSequenceLength++; + ch.endElement(null, null, SPAN); + ch.startElement(null, null, SPAN, lineAttribs); } else if (text[i] != '\r') { if (lineBreakSequenceLength > 1) { + ch.endElement(null, null, SPAN); ch.endElement(null, null, P); ch.startElement(null, null, P, null); + ch.startElement(null, null, SPAN, lineAttribs); } lineBreakSequenceLength = 0; @@ -257,6 +262,8 @@ else if (text[i] != '\r') { ch.characters(text, i, 1); } + + ch.endElement(null, null, SPAN); ch.endElement(null, null, P); } diff --git a/inception/inception-html-apache-annotator-editor/src/main/ts/src/apache-annotator/ApacheAnnotatorEditor.scss b/inception/inception-html-apache-annotator-editor/src/main/ts/src/apache-annotator/ApacheAnnotatorEditor.scss index 0edb54a802e..a8afa8b72a3 100644 --- a/inception/inception-html-apache-annotator-editor/src/main/ts/src/apache-annotator/ApacheAnnotatorEditor.scss +++ b/inception/inception-html-apache-annotator-editor/src/main/ts/src/apache-annotator/ApacheAnnotatorEditor.scss @@ -74,6 +74,10 @@ html > body { cursor: col-resize; } +.i7n-plain-text-document { + white-space: pre-line; +} + .i7n-wrapper { position: relative; line-height: 1.8; diff --git a/inception/inception-html-apache-annotator-editor/src/main/ts/src/apache-annotator/ApacheAnnotatorVisualizer.ts b/inception/inception-html-apache-annotator-editor/src/main/ts/src/apache-annotator/ApacheAnnotatorVisualizer.ts index 3e0a58c9c51..b414ac986e8 100644 --- a/inception/inception-html-apache-annotator-editor/src/main/ts/src/apache-annotator/ApacheAnnotatorVisualizer.ts +++ b/inception/inception-html-apache-annotator-editor/src/main/ts/src/apache-annotator/ApacheAnnotatorVisualizer.ts @@ -376,7 +376,7 @@ export class ApacheAnnotatorVisualizer { const startTime = new Date().getTime() this.renderHighlight(span, begin, end, attributes) const endTime = new Date().getTime() - console.debug(`Rendering span with size ${end - begin} took ${Math.abs(endTime - startTime)}ms`) + // console.debug(`Rendering span with size ${end - begin} took ${Math.abs(endTime - startTime)}ms`) } else { // Try optimizing for long spans to improve rendering performance let fragmentCount = 0 @@ -400,7 +400,7 @@ export class ApacheAnnotatorVisualizer { fragmentCount++ } const endTime = new Date().getTime() - console.debug(`Rendering span with size ${end - begin} took ${Math.abs(endTime - startTime)}ms (${fragmentCount} fragments)`) + // console.debug(`Rendering span with size ${end - begin} took ${Math.abs(endTime - startTime)}ms (${fragmentCount} fragments)`) } } diff --git a/inception/inception-js-api/src/main/ts/src/util/ViewportTracker.ts b/inception/inception-js-api/src/main/ts/src/util/ViewportTracker.ts index 9701f78d998..b197e095805 100644 --- a/inception/inception-js-api/src/main/ts/src/util/ViewportTracker.ts +++ b/inception/inception-js-api/src/main/ts/src/util/ViewportTracker.ts @@ -25,6 +25,7 @@ export type ViewportTrackerOptions = { } export const NO_TRACKING_CLASS = 'data-i7n-no-tracking' +export const TRACKING_CLASS = 'data-i7n-tracking' export class ViewportTracker { private _visibleElements = new Set() @@ -68,6 +69,10 @@ export class ViewportTracker { } private shouldTrack (element: Element): boolean { + if (element.classList.contains(TRACKING_CLASS)) { + return true + } + if (this.options?.ignoreSelector && element.matches(this.options.ignoreSelector)) { return false }