Merge pull request #12176 from calixteman/multiline
Support multiline textfields for printing
This commit is contained in:
		
						commit
						b061c300b4
					
				@ -958,11 +958,10 @@ class WidgetAnnotation extends Annotation {
 | 
				
			|||||||
    if (!annotationStorage || isPassword) {
 | 
					    if (!annotationStorage || isPassword) {
 | 
				
			||||||
      return null;
 | 
					      return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    let value = annotationStorage[this.data.id] || "";
 | 
					    const value = annotationStorage[this.data.id];
 | 
				
			||||||
    if (value === "") {
 | 
					    if (value === "") {
 | 
				
			||||||
      return null;
 | 
					      return "";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    value = escapeString(value);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const defaultPadding = 2;
 | 
					    const defaultPadding = 2;
 | 
				
			||||||
    const hPadding = defaultPadding;
 | 
					    const hPadding = defaultPadding;
 | 
				
			||||||
@ -983,12 +982,27 @@ class WidgetAnnotation extends Annotation {
 | 
				
			|||||||
    const vPadding = defaultPadding + Math.abs(descent) * fontSize;
 | 
					    const vPadding = defaultPadding + Math.abs(descent) * fontSize;
 | 
				
			||||||
    const defaultAppearance = this.data.defaultAppearance;
 | 
					    const defaultAppearance = this.data.defaultAppearance;
 | 
				
			||||||
    const alignment = this.data.textAlignment;
 | 
					    const alignment = this.data.textAlignment;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (this.data.multiLine) {
 | 
				
			||||||
 | 
					      return this._getMultilineAppearance(
 | 
				
			||||||
 | 
					        defaultAppearance,
 | 
				
			||||||
 | 
					        value,
 | 
				
			||||||
 | 
					        font,
 | 
				
			||||||
 | 
					        fontSize,
 | 
				
			||||||
 | 
					        totalWidth,
 | 
				
			||||||
 | 
					        totalHeight,
 | 
				
			||||||
 | 
					        alignment,
 | 
				
			||||||
 | 
					        hPadding,
 | 
				
			||||||
 | 
					        vPadding
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (alignment === 0 || alignment > 2) {
 | 
					    if (alignment === 0 || alignment > 2) {
 | 
				
			||||||
      // Left alignment: nothing to do
 | 
					      // Left alignment: nothing to do
 | 
				
			||||||
      return (
 | 
					      return (
 | 
				
			||||||
        "/Tx BMC q BT " +
 | 
					        "/Tx BMC q BT " +
 | 
				
			||||||
        defaultAppearance +
 | 
					        defaultAppearance +
 | 
				
			||||||
        ` 1 0 0 1 ${hPadding} ${vPadding} Tm (${value}) Tj` +
 | 
					        ` 1 0 0 1 ${hPadding} ${vPadding} Tm (${escapeString(value)}) Tj` +
 | 
				
			||||||
        " ET Q EMC"
 | 
					        " ET Q EMC"
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -1076,7 +1090,7 @@ class WidgetAnnotation extends Annotation {
 | 
				
			|||||||
    shift = shift.toFixed(2);
 | 
					    shift = shift.toFixed(2);
 | 
				
			||||||
    vPadding = vPadding.toFixed(2);
 | 
					    vPadding = vPadding.toFixed(2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return `${shift} ${vPadding} Td (${text}) Tj`;
 | 
					    return `${shift} ${vPadding} Td (${escapeString(text)}) Tj`;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1114,6 +1128,102 @@ class TextWidgetAnnotation extends WidgetAnnotation {
 | 
				
			|||||||
      !this.hasFieldFlag(AnnotationFieldFlag.FILESELECT) &&
 | 
					      !this.hasFieldFlag(AnnotationFieldFlag.FILESELECT) &&
 | 
				
			||||||
      this.data.maxLen !== null;
 | 
					      this.data.maxLen !== null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  _getMultilineAppearance(
 | 
				
			||||||
 | 
					    defaultAppearance,
 | 
				
			||||||
 | 
					    text,
 | 
				
			||||||
 | 
					    font,
 | 
				
			||||||
 | 
					    fontSize,
 | 
				
			||||||
 | 
					    width,
 | 
				
			||||||
 | 
					    height,
 | 
				
			||||||
 | 
					    alignment,
 | 
				
			||||||
 | 
					    hPadding,
 | 
				
			||||||
 | 
					    vPadding
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    const lines = text.split(/\r\n|\r|\n/);
 | 
				
			||||||
 | 
					    const buf = [];
 | 
				
			||||||
 | 
					    const totalWidth = width - 2 * hPadding;
 | 
				
			||||||
 | 
					    for (const line of lines) {
 | 
				
			||||||
 | 
					      const chunks = this._splitLine(line, font, fontSize, totalWidth);
 | 
				
			||||||
 | 
					      for (const chunk of chunks) {
 | 
				
			||||||
 | 
					        const padding = buf.length === 0 ? hPadding : 0;
 | 
				
			||||||
 | 
					        buf.push(
 | 
				
			||||||
 | 
					          this._renderText(
 | 
				
			||||||
 | 
					            chunk,
 | 
				
			||||||
 | 
					            font,
 | 
				
			||||||
 | 
					            fontSize,
 | 
				
			||||||
 | 
					            width,
 | 
				
			||||||
 | 
					            alignment,
 | 
				
			||||||
 | 
					            padding,
 | 
				
			||||||
 | 
					            -fontSize // <0 because a line is below the previous one
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const renderedText = buf.join("\n");
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      "/Tx BMC q BT " +
 | 
				
			||||||
 | 
					      defaultAppearance +
 | 
				
			||||||
 | 
					      ` 1 0 0 1 0 ${height} Tm ${renderedText}` +
 | 
				
			||||||
 | 
					      " ET Q EMC"
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  _splitLine(line, font, fontSize, width) {
 | 
				
			||||||
 | 
					    if (line.length <= 1) {
 | 
				
			||||||
 | 
					      // Nothing to split
 | 
				
			||||||
 | 
					      return [line];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const scale = fontSize / 1000;
 | 
				
			||||||
 | 
					    const whitespace = font.charsToGlyphs(" ", true)[0].width * scale;
 | 
				
			||||||
 | 
					    const chunks = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let lastSpacePos = -1,
 | 
				
			||||||
 | 
					      startChunk = 0,
 | 
				
			||||||
 | 
					      currentWidth = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (let i = 0, ii = line.length; i < ii; i++) {
 | 
				
			||||||
 | 
					      const character = line.charAt(i);
 | 
				
			||||||
 | 
					      if (character === " ") {
 | 
				
			||||||
 | 
					        if (currentWidth + whitespace > width) {
 | 
				
			||||||
 | 
					          // We can break here
 | 
				
			||||||
 | 
					          chunks.push(line.substring(startChunk, i));
 | 
				
			||||||
 | 
					          startChunk = i;
 | 
				
			||||||
 | 
					          currentWidth = whitespace;
 | 
				
			||||||
 | 
					          lastSpacePos = -1;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          currentWidth += whitespace;
 | 
				
			||||||
 | 
					          lastSpacePos = i;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        const charWidth = font.charsToGlyphs(character, false)[0].width * scale;
 | 
				
			||||||
 | 
					        if (currentWidth + charWidth > width) {
 | 
				
			||||||
 | 
					          // We must break to the last white position (if available)
 | 
				
			||||||
 | 
					          if (lastSpacePos !== -1) {
 | 
				
			||||||
 | 
					            chunks.push(line.substring(startChunk, lastSpacePos + 1));
 | 
				
			||||||
 | 
					            startChunk = i = lastSpacePos + 1;
 | 
				
			||||||
 | 
					            lastSpacePos = -1;
 | 
				
			||||||
 | 
					            currentWidth = 0;
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            // Just break in the middle of the word
 | 
				
			||||||
 | 
					            chunks.push(line.substring(startChunk, i));
 | 
				
			||||||
 | 
					            startChunk = i;
 | 
				
			||||||
 | 
					            currentWidth = charWidth;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          currentWidth += charWidth;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (startChunk < line.length) {
 | 
				
			||||||
 | 
					      chunks.push(line.substring(startChunk, line.length));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return chunks;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ButtonWidgetAnnotation extends WidgetAnnotation {
 | 
					class ButtonWidgetAnnotation extends WidgetAnnotation {
 | 
				
			||||||
 | 
				
			|||||||
@ -462,7 +462,7 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
 | 
				
			|||||||
        element.setAttribute("value", textContent);
 | 
					        element.setAttribute("value", textContent);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      element.addEventListener("change", function (event) {
 | 
					      element.addEventListener("input", function (event) {
 | 
				
			||||||
        storage.setValue(id, event.target.value);
 | 
					        storage.setValue(id, event.target.value);
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -689,7 +689,7 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
 | 
				
			|||||||
      selectElement.appendChild(optionElement);
 | 
					      selectElement.appendChild(optionElement);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    selectElement.addEventListener("change", function (event) {
 | 
					    selectElement.addEventListener("input", function (event) {
 | 
				
			||||||
      const options = event.target.options;
 | 
					      const options = event.target.options;
 | 
				
			||||||
      const value = options[options.selectedIndex].text;
 | 
					      const value = options[options.selectedIndex].text;
 | 
				
			||||||
      storage.setValue(id, value);
 | 
					      storage.setValue(id, value);
 | 
				
			||||||
 | 
				
			|||||||
@ -1673,6 +1673,114 @@ describe("annotation", function () {
 | 
				
			|||||||
          done();
 | 
					          done();
 | 
				
			||||||
        }, done.fail);
 | 
					        }, done.fail);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it("should render multiline text for printing", function (done) {
 | 
				
			||||||
 | 
					      textWidgetDict.set("Ff", AnnotationFieldFlag.MULTILINE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const textWidgetRef = Ref.get(271, 0);
 | 
				
			||||||
 | 
					      const xref = new XRefMock([
 | 
				
			||||||
 | 
					        { ref: textWidgetRef, data: textWidgetDict },
 | 
				
			||||||
 | 
					        fontRefObj,
 | 
				
			||||||
 | 
					      ]);
 | 
				
			||||||
 | 
					      const task = new WorkerTask("test print");
 | 
				
			||||||
 | 
					      partialEvaluator.xref = xref;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      AnnotationFactory.create(
 | 
				
			||||||
 | 
					        xref,
 | 
				
			||||||
 | 
					        textWidgetRef,
 | 
				
			||||||
 | 
					        pdfManagerMock,
 | 
				
			||||||
 | 
					        idFactoryMock
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					        .then(annotation => {
 | 
				
			||||||
 | 
					          const id = annotation.data.id;
 | 
				
			||||||
 | 
					          const annotationStorage = {};
 | 
				
			||||||
 | 
					          annotationStorage[id] =
 | 
				
			||||||
 | 
					            "a aa aaa aaaa aaaaa aaaaaa " +
 | 
				
			||||||
 | 
					            "pneumonoultramicroscopicsilicovolcanoconiosis";
 | 
				
			||||||
 | 
					          return annotation._getAppearance(
 | 
				
			||||||
 | 
					            partialEvaluator,
 | 
				
			||||||
 | 
					            task,
 | 
				
			||||||
 | 
					            annotationStorage
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        }, done.fail)
 | 
				
			||||||
 | 
					        .then(appearance => {
 | 
				
			||||||
 | 
					          expect(appearance).toEqual(
 | 
				
			||||||
 | 
					            "/Tx BMC q BT /Helv 5 Tf 1 0 0 1 0 10 Tm " +
 | 
				
			||||||
 | 
					              "2.00 -5.00 Td (a aa aaa ) Tj\n" +
 | 
				
			||||||
 | 
					              "0.00 -5.00 Td (aaaa aaaaa ) Tj\n" +
 | 
				
			||||||
 | 
					              "0.00 -5.00 Td (aaaaaa ) Tj\n" +
 | 
				
			||||||
 | 
					              "0.00 -5.00 Td (pneumonoultr) Tj\n" +
 | 
				
			||||||
 | 
					              "0.00 -5.00 Td (amicroscopi) Tj\n" +
 | 
				
			||||||
 | 
					              "0.00 -5.00 Td (csilicovolca) Tj\n" +
 | 
				
			||||||
 | 
					              "0.00 -5.00 Td (noconiosis) Tj ET Q EMC"
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
 | 
					        }, done.fail);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it("should render multiline text with various EOL for printing", function (done) {
 | 
				
			||||||
 | 
					      textWidgetDict.set("Ff", AnnotationFieldFlag.MULTILINE);
 | 
				
			||||||
 | 
					      textWidgetDict.set("Rect", [0, 0, 128, 10]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const textWidgetRef = Ref.get(271, 0);
 | 
				
			||||||
 | 
					      const xref = new XRefMock([
 | 
				
			||||||
 | 
					        { ref: textWidgetRef, data: textWidgetDict },
 | 
				
			||||||
 | 
					        fontRefObj,
 | 
				
			||||||
 | 
					      ]);
 | 
				
			||||||
 | 
					      const task = new WorkerTask("test print");
 | 
				
			||||||
 | 
					      partialEvaluator.xref = xref;
 | 
				
			||||||
 | 
					      const expectedAppearance =
 | 
				
			||||||
 | 
					        "/Tx BMC q BT /Helv 5 Tf 1 0 0 1 0 10 Tm " +
 | 
				
			||||||
 | 
					        "2.00 -5.00 Td " +
 | 
				
			||||||
 | 
					        "(Lorem ipsum dolor sit amet, consectetur adipiscing elit.) Tj\n" +
 | 
				
			||||||
 | 
					        "0.00 -5.00 Td " +
 | 
				
			||||||
 | 
					        "(Aliquam vitae felis ac lectus bibendum ultricies quis non) Tj\n" +
 | 
				
			||||||
 | 
					        "0.00 -5.00 Td " +
 | 
				
			||||||
 | 
					        "( diam.) Tj\n" +
 | 
				
			||||||
 | 
					        "0.00 -5.00 Td " +
 | 
				
			||||||
 | 
					        "(Morbi id porttitor quam, a iaculis dui.) Tj\n" +
 | 
				
			||||||
 | 
					        "0.00 -5.00 Td " +
 | 
				
			||||||
 | 
					        "(Pellentesque habitant morbi tristique senectus et netus ) Tj\n" +
 | 
				
			||||||
 | 
					        "0.00 -5.00 Td " +
 | 
				
			||||||
 | 
					        "(et malesuada fames ac turpis egestas.) Tj\n" +
 | 
				
			||||||
 | 
					        "0.00 -5.00 Td () Tj\n" +
 | 
				
			||||||
 | 
					        "0.00 -5.00 Td () Tj\n" +
 | 
				
			||||||
 | 
					        "0.00 -5.00 Td " +
 | 
				
			||||||
 | 
					        "(Nulla consectetur, ligula in tincidunt placerat, velit ) Tj\n" +
 | 
				
			||||||
 | 
					        "0.00 -5.00 Td " +
 | 
				
			||||||
 | 
					        "(augue consectetur orci, sed mattis libero nunc ut massa.) Tj\n" +
 | 
				
			||||||
 | 
					        "0.00 -5.00 Td " +
 | 
				
			||||||
 | 
					        "(Etiam facilisis tempus interdum.) Tj ET Q EMC";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      AnnotationFactory.create(
 | 
				
			||||||
 | 
					        xref,
 | 
				
			||||||
 | 
					        textWidgetRef,
 | 
				
			||||||
 | 
					        pdfManagerMock,
 | 
				
			||||||
 | 
					        idFactoryMock
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					        .then(annotation => {
 | 
				
			||||||
 | 
					          const id = annotation.data.id;
 | 
				
			||||||
 | 
					          const annotationStorage = {};
 | 
				
			||||||
 | 
					          annotationStorage[id] =
 | 
				
			||||||
 | 
					            "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\r" +
 | 
				
			||||||
 | 
					            "Aliquam vitae felis ac lectus bibendum ultricies quis non diam.\n" +
 | 
				
			||||||
 | 
					            "Morbi id porttitor quam, a iaculis dui.\r\n" +
 | 
				
			||||||
 | 
					            "Pellentesque habitant morbi tristique senectus et " +
 | 
				
			||||||
 | 
					            "netus et malesuada fames ac turpis egestas.\n\r\n\r" +
 | 
				
			||||||
 | 
					            "Nulla consectetur, ligula in tincidunt placerat, " +
 | 
				
			||||||
 | 
					            "velit augue consectetur orci, sed mattis libero nunc ut massa.\r" +
 | 
				
			||||||
 | 
					            "Etiam facilisis tempus interdum.";
 | 
				
			||||||
 | 
					          return annotation._getAppearance(
 | 
				
			||||||
 | 
					            partialEvaluator,
 | 
				
			||||||
 | 
					            task,
 | 
				
			||||||
 | 
					            annotationStorage
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        }, done.fail)
 | 
				
			||||||
 | 
					        .then(appearance => {
 | 
				
			||||||
 | 
					          expect(appearance).toEqual(expectedAppearance);
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
 | 
					        }, done.fail);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe("ButtonWidgetAnnotation", function () {
 | 
					  describe("ButtonWidgetAnnotation", function () {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user