123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- /* eslint-disable no-param-reassign */
- import { v4 as uuidv4 } from 'uuid';
- import dayjs from 'dayjs';
- import queryString from 'query-string';
- import { ANNOTATION_TYPE, FORM_TYPE } from '../constants';
- import { getPdfPage, renderTextLayer } from './pdf';
- import { getPosition, parsePositionForBackend } from './position';
- import { normalizeRound, floatToHex } from './utility';
- import { xmlParser, getElementsByTagName } from './dom';
- type GetFontAttributeFunc = (
- type: string,
- element: HTMLElement,
- ) => Record<string, unknown>;
- const getContent = (type: string, element: HTMLElement): string => {
- if (type !== 'Text' && type !== 'FreeText') return '';
- let content = '';
- const nodes = Array.prototype.slice.call(element.childNodes);
- nodes.forEach((ele: HTMLElement) => {
- if (ele.tagName === 'contents') {
- content = ele.innerHTML || ele.textContent || '';
- }
- });
- return content;
- };
- const getFontAttribute: GetFontAttributeFunc = (type, element) => {
- if (type !== 'FreeText') return {};
- const appearanceString =
- (element.childNodes[1] as HTMLElement).innerHTML ||
- element.childNodes[1].textContent ||
- '';
- const arr = appearanceString.split(' ');
- return {
- fontsize: parseInt(arr[5], 10),
- fontname: arr[4].substr(1),
- textcolor: floatToHex(
- parseFloat(arr[0]),
- parseFloat(arr[1]),
- parseFloat(arr[2]),
- ),
- };
- };
- export const parseAnnotationFromXml = (xmlString: string): AnnotationType[] => {
- if (!xmlString) return [];
- const xmlDoc = xmlParser(xmlString);
- const elements = xmlDoc.firstElementChild || xmlDoc.firstChild;
- const element = getElementsByTagName(elements as ChildNode, 'annots') || [];
- const annotations: HTMLElement[] = Array.prototype.slice.call(element);
- const filterAnnots = annotations.reduce(
- (acc: AnnotationType[], cur: HTMLElement) => {
- const type = ANNOTATION_TYPE[cur.tagName];
- const attributes = cur.attributes as ElementAttributeType;
- if (type) {
- const page = parseInt(attributes.page.value, 10);
- acc.push({
- id: uuidv4(),
- obj_type: type,
- obj_attr: {
- title: attributes.title ? attributes.title.value : undefined,
- date: attributes.date ? attributes.date.value : undefined,
- page,
- position: getPosition(type, cur),
- bdcolor: attributes.color ? attributes.color.value : undefined,
- bdwidth: attributes.width
- ? parseInt(attributes.width.value, 10)
- : 0,
- transparency: attributes.opacity
- ? parseFloat(attributes.opacity.value)
- : 1,
- content: getContent(type, cur) || undefined,
- fcolor: attributes['interior-color']
- ? attributes['interior-color'].value
- : undefined,
- ftransparency: attributes['interior-opacity']
- ? parseFloat(attributes['interior-opacity'].value)
- : undefined,
- is_arrow: !!attributes.tail,
- ...getFontAttribute(type, cur),
- },
- });
- }
- return acc;
- },
- [],
- );
- return filterAnnots;
- };
- export const parseFormElementFromXml = (
- xmlString: string,
- ): AnnotationType[] => {
- if (!xmlString) return [];
- const xmlDoc = xmlParser(xmlString);
- const elements = xmlDoc.firstElementChild || xmlDoc.firstChild;
- const element = getElementsByTagName(elements as ChildNode, 'widgets') || [];
- const annotations: HTMLElement[] = Array.prototype.slice.call(element);
- const filterForm = annotations.reduce(
- (acc: AnnotationType[], cur: HTMLElement) => {
- const type = FORM_TYPE[cur.tagName];
- const attributes = cur.attributes as ElementAttributeType;
- if (type) {
- const page = parseInt(attributes.page.value, 10);
- acc.push({
- id: uuidv4(),
- obj_type: type,
- obj_attr: {
- date: attributes.date ? attributes.date.value : undefined,
- page,
- position: getPosition(type, cur),
- bdcolor: attributes.color ? attributes.color.value : undefined,
- style: attributes.style ? attributes.style.value : undefined,
- bdwidth: attributes.width
- ? parseInt(attributes.width.value, 10)
- : 0,
- transparency: attributes.opacity
- ? parseFloat(attributes.opacity.value)
- : 1,
- },
- });
- }
- return acc;
- },
- [],
- );
- return filterForm;
- };
- // eslint-disable-next-line consistent-return
- const getEleText = (
- coord: PositionType,
- elements: HTMLElement[],
- viewport: ViewportType,
- scale: number,
- ): string => {
- const top = normalizeRound(viewport.height - coord.top * scale);
- const left = normalizeRound(coord.left * scale);
- const bottom = normalizeRound(viewport.height - coord.bottom * scale);
- const right = normalizeRound(coord.right * scale);
- for (let i = 0, len = elements.length; i <= len; i += 1) {
- const element = elements[i];
- if (element) {
- const eleTop = normalizeRound(element.offsetTop);
- const eleLeft = normalizeRound(element.offsetLeft);
- const eleRight = normalizeRound(element.offsetLeft + element.offsetWidth);
- if (eleTop >= top && eleTop <= bottom) {
- const textLength = element.innerText.length;
- const width = element.offsetWidth;
- if (eleLeft < left && eleRight > right) {
- const distanceL = left - eleLeft;
- const rateL = distanceL / width;
- const start = Math.floor(textLength * rateL);
- const distanceR = eleRight - right;
- const rateR = distanceR / width;
- const end = Math.floor(textLength - textLength * rateR);
- return ` ${element.innerText.slice(start, end)}`;
- }
- if (eleLeft < left && eleRight > left) {
- const distance = left - eleLeft;
- const rate = distance / width;
- const start = Math.floor(textLength * rate);
- return ` ${element.innerText.slice(start)}`;
- }
- if (eleRight > right && eleLeft < right) {
- const distance = eleRight - right;
- const rate = distance / width;
- const end = Math.floor(textLength - textLength * rate);
- return ` ${element.innerText.slice(0, end)}`;
- }
- if (eleLeft >= left && eleRight <= right) {
- return ` ${element.innerText}`;
- }
- }
- }
- }
- return '';
- };
- export const getAnnotationText = async ({
- viewport,
- scale,
- page,
- coords,
- pdf,
- }: {
- viewport: ViewportType;
- scale: number;
- page: number;
- coords: PositionType[];
- pdf: PdfType;
- }): Promise<string> => {
- const pageContainer = document.getElementById(`page_${page}`) as HTMLElement;
- const textLayer = pageContainer.querySelector(
- '[data-id="text-layer"]',
- ) as HTMLElement;
- const pdfPage = await getPdfPage(pdf, page);
- if (!textLayer.childNodes.length) {
- await renderTextLayer({
- textLayer,
- pdfPage,
- viewport,
- });
- }
- const textElements = Array.prototype.slice.call(textLayer.childNodes);
- let text = '';
- for (let i = 0, len = coords.length; i < len; i += 1) {
- const coord = coords[i];
- text += getEleText(coord, textElements, viewport, scale);
- }
- return text;
- };
- export const parseAnnotationObject = (
- {
- id,
- obj_type,
- obj_attr: {
- page,
- bdcolor,
- transparency,
- fcolor,
- ftransparency,
- position = { left: 0, top: 0, right: 0, bottom: 0 },
- content,
- style,
- bdwidth,
- fontname,
- fontsize,
- textcolor,
- is_arrow,
- src,
- },
- }: AnnotationType,
- pageHeight: number,
- scale: number,
- ): AnnotationType => ({
- id: id || uuidv4(),
- obj_type,
- obj_attr: {
- page: page - 1,
- bdcolor,
- position: parsePositionForBackend(obj_type, position, pageHeight, scale),
- transparency: transparency ? transparency * 0.01 : 0,
- content: content || undefined,
- style,
- fcolor,
- ftransparency: ftransparency ? ftransparency * 0.01 : 0,
- bdwidth,
- fontname,
- fontsize,
- textcolor,
- is_arrow,
- src,
- },
- });
- export const appendUserIdAndDate = (
- annotateObj: AnnotationType,
- ): AnnotationType => {
- const parsed = queryString.parse(window.location.search);
- if (parsed.watermark) {
- annotateObj.obj_attr.title = parsed.watermark;
- }
- const datetime = dayjs().format('YYYY-MM-DD_HH:mm:ss');
- annotateObj.obj_attr.date = datetime;
- return annotateObj;
- };
|