protected void drawHyperlink( final RenderNode box, final String target, final String window, final String title) { if (box.isNodeVisible(getDrawArea()) == false) { return; } final PdfAction action = createActionForLink(target); final AffineTransform affineTransform = getGraphics().getTransform(); final float translateX = (float) affineTransform.getTranslateX(); final float leftX = translateX + (float) (StrictGeomUtility.toExternalValue(box.getX())); final float rightX = translateX + (float) (StrictGeomUtility.toExternalValue(box.getX() + box.getWidth())); final float lowerY = (float) (globalHeight - StrictGeomUtility.toExternalValue(box.getY() + box.getHeight())); final float upperY = (float) (globalHeight - StrictGeomUtility.toExternalValue(box.getY())); if (action != null) { final PdfAnnotation annotation = new PdfAnnotation(writer, leftX, lowerY, rightX, upperY, action); writer.addAnnotation(annotation); } else if (StringUtils.isEmpty(title) == false) { final Rectangle rect = new Rectangle(leftX, lowerY, rightX, upperY); final PdfAnnotation commentAnnotation = PdfAnnotation.createText(writer, rect, "Tooltip", title, false, null); commentAnnotation.setAppearance( PdfAnnotation.APPEARANCE_NORMAL, writer.getDirectContent().createAppearance(rect.getWidth(), rect.getHeight())); writer.addAnnotation(commentAnnotation); } }
protected boolean drawPdfScript(final RenderNode box) { final Object attribute = box.getAttributes() .getAttribute(AttributeNames.Pdf.NAMESPACE, AttributeNames.Pdf.SCRIPT_ACTION); if (attribute == null) { return false; } final String attributeText = String.valueOf(attribute); final PdfAction action = PdfAction.javaScript(attributeText, writer, false); final AffineTransform affineTransform = getGraphics().getTransform(); final float translateX = (float) affineTransform.getTranslateX(); final float leftX = translateX + (float) (StrictGeomUtility.toExternalValue(box.getX())); final float rightX = translateX + (float) (StrictGeomUtility.toExternalValue(box.getX() + box.getWidth())); final float lowerY = (float) (globalHeight - StrictGeomUtility.toExternalValue(box.getY() + box.getHeight())); final float upperY = (float) (globalHeight - StrictGeomUtility.toExternalValue(box.getY())); final PdfAnnotation annotation = new PdfAnnotation(writer, leftX, lowerY, rightX, upperY, action); writer.addAnnotation(annotation); return true; }
public void processLogicalPage(final LogicalPageKey key, final LogicalPageBox logicalPage) throws DocumentException { final float width = (float) StrictGeomUtility.toExternalValue(logicalPage.getPageWidth()); final float height = (float) StrictGeomUtility.toExternalValue(logicalPage.getPageHeight()); final Rectangle pageSize = new Rectangle(width, height); final Document document = getDocument(); document.setPageSize(pageSize); document.setMargins(0, 0, 0, 0); if (awaitOpenDocument) { document.open(); awaitOpenDocument = false; } final Graphics2D graphics = new PdfGraphics2D(writer.getDirectContent(), width, height, metaData); // and now process the box .. final PdfLogicalPageDrawable logicalPageDrawable = new PdfLogicalPageDrawable( logicalPage, metaData, writer, null, resourceManager, imageCache, version); logicalPageDrawable.draw(graphics, new Rectangle2D.Double(0, 0, width, height)); graphics.dispose(); document.newPage(); }
protected void drawImageMap(final RenderableReplacedContentBox content) { if (version < '6') { return; } final ImageMap imageMap = RenderUtility.extractImageMap(content); // only generate a image map, if the user does not specify their own onw via the override. // Of course, they would have to provide the map by other means as well. if (imageMap == null) { return; } final ImageMapEntry[] imageMapEntries = imageMap.getMapEntries(); for (int i = 0; i < imageMapEntries.length; i++) { final ImageMapEntry imageMapEntry = imageMapEntries[i]; final String link = imageMapEntry.getAttribute(LibXmlInfo.XHTML_NAMESPACE, "href"); final String tooltip = imageMapEntry.getAttribute(LibXmlInfo.XHTML_NAMESPACE, "title"); if (StringUtils.isEmpty(tooltip)) { continue; } final AffineTransform affineTransform = getGraphics().getTransform(); final float translateX = (float) affineTransform.getTranslateX(); final int x = (int) (translateX + StrictGeomUtility.toExternalValue(content.getX())); final int y = (int) StrictGeomUtility.toExternalValue(content.getY()); final float[] translatedCoords = translateCoordinates(imageMapEntry.getAreaCoordinates(), x, y); final PolygonAnnotation polygonAnnotation = new PolygonAnnotation(writer, translatedCoords); polygonAnnotation.put(PdfName.CONTENTS, new PdfString(tooltip, PdfObject.TEXT_UNICODE)); writer.addAnnotation(polygonAnnotation); } }
private void updateCellStyle(final Cell cell, final CellBackground background) { final Color backgroundColor = background.getBackgroundColor(); if (backgroundColor != null) { cell.setBackgroundColor(backgroundColor); } final BorderEdge top = background.getTop(); if (BorderEdge.EMPTY.equals(top) == false) { cell.setBorderColorTop(top.getColor()); cell.setBorderWidthTop((float) StrictGeomUtility.toExternalValue(top.getWidth())); } final BorderEdge left = background.getLeft(); if (BorderEdge.EMPTY.equals(left) == false) { cell.setBorderColorLeft(left.getColor()); cell.setBorderWidthLeft((float) StrictGeomUtility.toExternalValue(left.getWidth())); } final BorderEdge bottom = background.getBottom(); if (BorderEdge.EMPTY.equals(bottom) == false) { cell.setBorderColorBottom(bottom.getColor()); cell.setBorderWidthBottom((float) StrictGeomUtility.toExternalValue(bottom.getWidth())); } final BorderEdge right = background.getRight(); if (BorderEdge.EMPTY.equals(right) == false) { cell.setBorderColorRight(right.getColor()); cell.setBorderWidthRight((float) StrictGeomUtility.toExternalValue(right.getWidth())); } }
public void resizeProportional() { final float originalPageWidth = originalPageDefinition.getWidth(); final float currentPageWidth = currentPageDefinition.getWidth(); final float scaleFactor = currentPageWidth / originalPageWidth; for (int i = 0; i < visualElements.length; i++) { // Resize the element. final CachedLayoutData cachedLayoutData = ModelUtility.getCachedLayoutData(visualElements[i]); final double elementWidth = StrictGeomUtility.toExternalValue(cachedLayoutData.getWidth()); final Element theElement = visualElements[i]; final ElementStyleSheet styleSheet = theElement.getStyle(); styleSheet.setStyleProperty( ElementStyleKeys.MIN_WIDTH, new Float(elementWidth * scaleFactor)); // Reposition the element. final double origin = StrictGeomUtility.toExternalValue(cachedLayoutData.getX()); final double destination = scaleFactor * origin; final int theShift = (int) (destination - origin); final Element[] theElements = new Element[1]; theElements[0] = theElement; align(theShift, theElements); } registerChanges(); }
public void testOrphan4() throws Exception { final MasterReport masterReport = DebugReportRunner.parseGoldenSampleReport("Prd-2087-Orphan-4.prpt"); final LogicalPageBox box = DebugReportRunner.layoutPage(masterReport, 0); final RenderNode srs[] = MatchFactory.findElementsByElementType(box, SubReportType.INSTANCE); assertEquals(1, srs.length); assertEquals(StrictGeomUtility.toInternalValue(20), srs[0].getY()); final RenderNode elementByName = MatchFactory.findElementByName(box, "outer-group"); assertEquals(StrictGeomUtility.toInternalValue(20), elementByName.getY()); }
public void alignRight() { final double theCurrentPageWidth = currentPageDefinition.getWidth(); final int theShiftRight = (int) (theCurrentPageWidth - StrictGeomUtility.toExternalValue(computeFarRightPostion())); align(theShiftRight, visualElements); registerChanges(); }
@Test public void testCanvasWithPrePostPad() throws Exception { final MasterReport report = new MasterReport(); report.setDataFactory(new TableDataFactory("query", new DefaultTableModel(10, 1))); report.setQuery("query"); final Band table = TableTestUtil.createTable(1, 1, 6, new CustomProducer()); table.getStyle().setStyleProperty(ElementStyleKeys.MIN_WIDTH, 200f); table.getStyle().setStyleProperty(ElementStyleKeys.POS_X, 100f); table.getStyle().setStyleProperty(ElementStyleKeys.POS_Y, 10f); table.setName("table"); report.getReportHeader().addElement(TableTestUtil.createDataItem("Pre-Padding", 100, 10)); report.getReportHeader().addElement(table); Element postPaddingItem = TableTestUtil.createDataItem("Post-Padding", 100, 10); postPaddingItem.getStyle().setStyleProperty(ElementStyleKeys.POS_X, 300f); report.getReportHeader().addElement(postPaddingItem); report.getReportHeader().setLayout("canvas"); PdfReportUtil.createPDF(report, "test-output/PRD-3857-output-canvas.pdf"); List<LogicalPageBox> pages = DebugReportRunner.layoutPages(report, 0, 1, 2); assertPageValid(pages, 0, StrictGeomUtility.toInternalValue(10)); assertPageValid(pages, 1); assertPageValid(pages, 2); }
protected void drawBookmark(final RenderNode box, final String bookmark) { if (box.isNodeVisible(getDrawArea()) == false) { return; } final PdfOutline root = writer.getDirectContent().getRootOutline(); final AffineTransform affineTransform = getGraphics().getTransform(); final float translateX = (float) affineTransform.getTranslateX(); final float upperY = translateX + (float) (globalHeight - StrictGeomUtility.toExternalValue(box.getY())); final float leftX = (float) (StrictGeomUtility.toExternalValue(box.getX())); final PdfDestination dest = new PdfDestination(PdfDestination.FIT, leftX, upperY, 0); new PdfOutline(root, dest, bookmark); // destination will always point to the 'current' page // todo: Make this a hierarchy .. }
protected void drawAnchor(final RenderNode content) { if (content.isNodeVisible(getDrawArea()) == false) { return; } final String anchorName = (String) content.getStyleSheet().getStyleProperty(ElementStyleKeys.ANCHOR_NAME); if (anchorName == null) { return; } final AffineTransform affineTransform = getGraphics().getTransform(); final float translateX = (float) affineTransform.getTranslateX(); final float upperY = translateX + (float) (globalHeight - StrictGeomUtility.toExternalValue(content.getY())); final float leftX = (float) (StrictGeomUtility.toExternalValue(content.getX())); final PdfDestination dest = new PdfDestination(PdfDestination.FIT, leftX, upperY, 0); writer.getDirectContent().localDestination(anchorName, dest); }
public void testPagebreakHonoredOnFirstPage() throws Exception { final MasterReport masterReport = DebugReportRunner.parseGoldenSampleReport("Pre-492.prpt"); final LogicalPageBox page0 = DebugReportRunner.layoutPage(masterReport, 0); final RenderNode[] elementsByElementType = MatchFactory.findElementsByElementType(page0.getContentArea(), AutoLayoutBoxType.INSTANCE); assertEquals(31, elementsByElementType.length); assertEquals( StrictGeomUtility.toInternalValue(199), elementsByElementType[elementsByElementType.length - 1].getY()); final LogicalPageBox page1 = DebugReportRunner.layoutPage(masterReport, 1); final RenderNode[] elementsPage1 = MatchFactory.findElementsByElementType(page1.getContentArea(), AutoLayoutBoxType.INSTANCE); assertEquals(34, elementsPage1.length); assertEquals( StrictGeomUtility.toInternalValue(211), elementsPage1[elementsPage1.length - 1].getY()); // ModelPrinter.INSTANCE.print(page1); }
/** Invoked when an action occurs. */ public void actionPerformed(final ActionEvent e) { final ReportSelectionModel model = getSelectionModel(); if (model == null) { return; } final Element[] visualElements = model.getSelectedVisualElements(); if (visualElements.length <= 1) { return; } final Element[] carrier = new Element[1]; final Element[] objects = ModelUtility.filterParents(visualElements); final MassElementStyleUndoEntryBuilder builder = new MassElementStyleUndoEntryBuilder(objects); long minY = Long.MAX_VALUE; long maxY = Long.MIN_VALUE; for (int j = 0; j < objects.length; j++) { final Element object = objects[j]; final CachedLayoutData data = ModelUtility.getCachedLayoutData(object); final long y1 = data.getY(); final long y2 = y1 + data.getHeight(); if (y2 > maxY) { maxY = y2; } if (y1 < minY) { minY = y1; } } final long centerPoint = minY + (maxY - minY) / 2; for (int j = 0; j < objects.length; j++) { final Element object = objects[j]; final CachedLayoutData data = ModelUtility.getCachedLayoutData(object); final long elementCenter = data.getY() + data.getHeight() / 2; final long delta = centerPoint - elementCenter; if (delta == 0) { continue; } carrier[0] = object; final MoveDragOperation mop = new MoveDragOperation( carrier, ORIGIN_POINT, EmptySnapModel.INSTANCE, EmptySnapModel.INSTANCE); mop.update(new Point2D.Double(0, StrictGeomUtility.toExternalValue(delta)), 1); mop.finish(); } final MassElementStyleUndoEntry massElementStyleUndoEntry = builder.finish(); getActiveContext() .getUndo() .addChange( ActionMessages.getString("AlignMiddleAction.UndoName"), massElementStyleUndoEntry); }
public void alignCenter() { final long farLeftPostion = computeFarLeftPosition(); final long farRightPostion = computeFarRightPostion(); final long currentPageWidth = StrictGeomUtility.toInternalValue(currentPageDefinition.getWidth()); final long remainingRightSpace = currentPageWidth - farRightPostion; final long normalizedSpace = (farLeftPostion + remainingRightSpace) / 2; long requiredShift = normalizedSpace - farLeftPostion; if (remainingRightSpace > farLeftPostion) { // move to the Right requiredShift = Math.abs(requiredShift); } else { // move to the Left requiredShift = 0 - Math.abs(requiredShift); } final int shiftInPoints = (int) StrictGeomUtility.toExternalValue(requiredShift); align(shiftInPoints, visualElements); registerChanges(); }
/** * Canvas elements do not shift content. Therefore the widow definition is not effective. * * @throws Exception */ public void testOrphan5() throws Exception { final MasterReport masterReport = DebugReportRunner.parseGoldenSampleReport("Prd-2087-Orphan-5.prpt"); // masterReport.setCompatibilityLevel(ClassicEngineBoot.computeVersionId(3, 8, 0)); // DebugReportRunner.createXmlPageable(masterReport); final LogicalPageBox box = DebugReportRunner.layoutPage(masterReport, 0); final RenderNode elementByName = MatchFactory.findElementByName(box, "outer-group"); assertEquals(StrictGeomUtility.toInternalValue(20), elementByName.getY()); // ModelPrinter.INSTANCE.print(box); // DebugReportRunner.showDialog(masterReport); }
public void init( final LogicalPageBox rootBox, final PdfOutputProcessorMetaData metaData, final ResourceManager resourceManager, final PhysicalPageBox page) { super.init(rootBox, metaData, resourceManager); if (page != null) { this.globalHeight = (float) StrictGeomUtility.toExternalValue( page.getHeight() - page.getImageableY() + page.getGlobalY()); } else { this.globalHeight = rootBox.getPageHeight(); } this.globalEmbed = getMetaData().isFeatureSupported(OutputProcessorFeature.EMBED_ALL_FONTS); }
public void processPhysicalPage( final PageGrid pageGrid, final LogicalPageBox logicalPage, final int row, final int col, final PhysicalPageKey pageKey) throws DocumentException { final PhysicalPageBox page = pageGrid.getPage(row, col); if (page == null) { return; } final float width = (float) StrictGeomUtility.toExternalValue(page.getWidth()); final float height = (float) StrictGeomUtility.toExternalValue(page.getHeight()); final Rectangle pageSize = new Rectangle(width, height); final float marginLeft = (float) StrictGeomUtility.toExternalValue(page.getImageableX()); final float marginRight = (float) StrictGeomUtility.toExternalValue( page.getWidth() - page.getImageableWidth() - page.getImageableX()); final float marginTop = (float) StrictGeomUtility.toExternalValue(page.getImageableY()); final float marginBottom = (float) StrictGeomUtility.toExternalValue( page.getHeight() - page.getImageableHeight() - page.getImageableY()); final Document document = getDocument(); document.setPageSize(pageSize); document.setMargins(marginLeft, marginRight, marginTop, marginBottom); if (awaitOpenDocument) { document.open(); awaitOpenDocument = false; } final PdfContentByte directContent = writer.getDirectContent(); final Graphics2D graphics = new PdfGraphics2D(directContent, width, height, metaData); final PdfLogicalPageDrawable logicalPageDrawable = new PdfLogicalPageDrawable( logicalPage, metaData, writer, page, resourceManager, imageCache, version); final PhysicalPageDrawable drawable = new PhysicalPageDrawable(logicalPageDrawable, page); drawable.draw(graphics, new Rectangle2D.Double(0, 0, width, height)); graphics.dispose(); document.newPage(); }
@Test public void testBlockWithPrePostPad() throws Exception { final MasterReport report = new MasterReport(); report.setDataFactory(new TableDataFactory("query", new DefaultTableModel(10, 1))); report.setQuery("query"); final Band table = TableTestUtil.createTable(1, 1, 6, true); table.setName("table"); report.getReportHeader().addElement(TableTestUtil.createDataItem("Pre-Padding", 100, 10)); report.getReportHeader().addElement(table); report.getReportHeader().addElement(TableTestUtil.createDataItem("Post-Padding", 100, 10)); report.getReportHeader().setLayout("block"); PdfReportUtil.createPDF(report, "test-output/PRD-3857-output-block.pdf"); List<LogicalPageBox> pages = DebugReportRunner.layoutPages(report, 0, 1, 2); assertPageValid(pages, 0, StrictGeomUtility.toInternalValue(10)); assertPageValid(pages, 1); assertPageValid(pages, 2); // assertPageValid(report, 3); // assertPageValid(report, 4); }
/** * Another static processing step which validates the table structure and computes the cell * positions within the table. * * @author Thomas Morgner */ public class TableValidationStep extends IterateStructuralProcessStep { private static final long MAX_AUTO = StrictGeomUtility.toInternalValue(0x80000000000L); private static class TableInfoStructure { private TableRenderBox table; private TableInfoStructure parent; private TableColumnModel columnModel; private TableSectionRenderBox sectionRenderBox; private IntList rowSpans; private TableRowModel rowModel; protected int tableCellPosition; protected int rowCount; private boolean bodySection; public TableInfoStructure(final TableRenderBox table, final TableInfoStructure parent) { this.table = table; this.parent = parent; this.columnModel = table.getColumnModel(); this.rowSpans = new IntList(10); } public void resetCellPosition() { this.tableCellPosition = 0; } private boolean isCellAreaClear(final int pos, final int colSpan) { final int maxIdx = Math.min(pos + colSpan, rowSpans.size()); for (int i = pos; i < maxIdx; i++) { if (rowSpans.get(tableCellPosition) > 0) { return false; } } return true; } public int increaseCellPosition(final int colSpan, final int rowSpan) { // find insert-position for the cell. This skips cells that block the location via a row-span. while (true) { // we are past the point of defined cells. Adding new cells is guaranteed to not have // row-spans. if (tableCellPosition >= rowSpans.size()) { break; } if (isCellAreaClear(tableCellPosition, colSpan)) { break; } tableCellPosition += 1; } final int retval = tableCellPosition; // set the cell... for (int i = tableCellPosition; i < tableCellPosition + colSpan; i++) { if (i < rowSpans.size()) { rowSpans.set(i, Math.max(rowSpan, rowSpans.get(i))); } else { rowSpans.add(rowSpan); } } tableCellPosition += colSpan; return retval; } public TableInfoStructure pop() { return parent; } public TableSectionRenderBox getSectionRenderBox() { return sectionRenderBox; } public void setSectionRenderBox(final TableSectionRenderBox sectionRenderBox) { this.rowSpans.clear(); this.sectionRenderBox = sectionRenderBox; if (this.sectionRenderBox != null) { this.rowModel = sectionRenderBox.getRowModel(); this.bodySection = (sectionRenderBox.getDisplayRole() == TableSectionRenderBox.Role.BODY); } else { this.rowModel = null; this.bodySection = false; } this.rowCount = -1; } public boolean isBodySection() { return bodySection; } public TableRenderBox getTable() { return table; } public TableColumnModel getColumnModel() { return columnModel; } public void updateDefinedSize(final int rowSpan, final long preferredSize) { rowModel.updateDefinedSize(rowCount, rowSpan, preferredSize); } } private TableInfoStructure currentTable; private TableColumnGroup currentColumnGroup; public TableValidationStep() {} public void validate(final LogicalPageBox box) { currentTable = null; startProcessing(box); if (currentTable != null) { throw new IllegalStateException(); } } private boolean abortIfNoTable(final RenderBox box) { if (box.getTableRefCount() == 0) { return false; } if (box.getTableValidationAge() == box.getChangeTracker()) { return false; } box.setTableValidationAge(box.getChangeTracker()); return true; } protected boolean startCanvasBox(final CanvasRenderBox box) { return abortIfNoTable(box); } protected boolean startBlockBox(final BlockRenderBox box) { return abortIfNoTable(box); } protected boolean startInlineBox(final InlineRenderBox box) { return abortIfNoTable(box); } protected boolean startOtherBox(final RenderBox box) { return abortIfNoTable(box); } protected boolean startRowBox(final RenderBox box) { return abortIfNoTable(box); } protected boolean startAutoBox(final RenderBox box) { if (currentTable != null) { return true; } return abortIfNoTable(box); } protected boolean startTableBox(final TableRenderBox table) { final long changeTracker = table.getChangeTracker(); final long age = table.getTableValidationAge(); if (changeTracker == age) { return false; } currentTable = new TableInfoStructure(table, currentTable); return true; } protected void finishTableBox(final TableRenderBox table) { final long changeTracker = table.getChangeTracker(); final long age = table.getTableValidationAge(); if (changeTracker == age) { return; } // currentTable.columnModel.validateSizes(table); table.setTableValidationAge(age); table.setPredefinedColumnsValidated(true); currentTable = currentTable.pop(); } protected boolean startTableColumnGroupBox(final TableColumnGroupNode box) { if (currentTable == null) { return false; } if (currentTable.table.isPredefinedColumnsValidated()) { return false; } currentColumnGroup = new TableColumnGroup(box.getBoxDefinition().getBorder()); currentColumnGroup.setColSpan(box.getColSpan()); return true; } protected void processTableColumn(final TableColumnNode node) { if (currentTable == null) { return; } if (currentTable.table.isPredefinedColumnsValidated()) { return; } final Border border = node.getBoxDefinition().getBorder(); final RenderLength length = node.getBoxDefinition().getMinimumWidth(); if (currentColumnGroup != null) { currentColumnGroup.addColumn(new TableColumn(border, length, false)); } else { final TableColumnGroup currentColumnGroup = new TableColumnGroup(BoxDefinition.EMPTY.getBorder()); currentColumnGroup.addColumn(new TableColumn(border, length, false)); currentTable.columnModel.addColumnGroup(currentColumnGroup); } } protected void finishTableColumnGroupBox(final TableColumnGroupNode box) { if (currentTable == null) { return; } if (currentTable.table.isPredefinedColumnsValidated()) { return; } while (currentColumnGroup.getColumnCount() < box.getColSpan()) { currentColumnGroup.addColumn( new TableColumn(currentColumnGroup.getBorder(), RenderLength.AUTO, false)); } currentTable.columnModel.addColumnGroup(currentColumnGroup); currentColumnGroup = null; } protected boolean startTableSectionBox(final TableSectionRenderBox box) { if (currentTable == null) { return false; } if (currentTable.getSectionRenderBox() != null) { return true; } currentTable.setSectionRenderBox(box); box.getRowModel().initialize(currentTable.getTable()); return true; } protected void finishTableSectionBox(final TableSectionRenderBox box) { if (currentTable == null) { return; } if (currentTable.getSectionRenderBox() != box) { return; } final IntList rowSpans = currentTable.rowSpans; int missingRows = 0; for (int i = 0; i < rowSpans.size(); i++) { final int value = rowSpans.get(i); if (missingRows < value) { missingRows = value; } } for (int i = 0; i < missingRows; i += 1) { currentTable.rowModel.addRow(); } box.getRowModel().validateSizes(box); currentTable.setSectionRenderBox(null); } protected boolean startTableRowBox(final TableRowRenderBox box) { if (currentTable == null) { return false; } if (currentTable.getSectionRenderBox() == null) { return false; } currentTable.resetCellPosition(); box.setBodySection(currentTable.isBodySection()); // check if this is the first row ... if (currentTable.rowCount == -1) { if (box.getRowIndex() != -1) { currentTable.rowCount = box.getRowIndex(); return true; } } currentTable.rowCount += 1; box.setRowIndex(currentTable.rowCount); if (currentTable.rowCount <= currentTable.rowModel.getRowCount()) { currentTable.rowModel.addRow(); } return true; } protected void finishTableRowBox(final TableRowRenderBox box) { if (currentTable == null) { return; } if (currentTable.getSectionRenderBox() == null) { return; } final IntList rowSpans = currentTable.rowSpans; int maxRowSpan = 0; for (int i = 0; i < rowSpans.size(); i++) { final int value = rowSpans.get(i); maxRowSpan = Math.max(maxRowSpan, value); } for (int i = 0; i < rowSpans.size(); i++) { final int value = rowSpans.get(i); rowSpans.set(i, Math.max(0, value - 1)); } } protected boolean startTableCellBox(final TableCellRenderBox box) { if (currentTable == null) { return false; } if (currentTable.getSectionRenderBox() == null) { return false; } final int rowSpan = box.getRowSpan(); final int colSpan = box.getColSpan(); final int startPos = currentTable.increaseCellPosition(colSpan, rowSpan); while (currentTable.columnModel.getColumnCount() <= startPos) { currentTable.columnModel.addAutoColumn(); } box.setColumnIndex(startPos); box.setBodySection(currentTable.isBodySection()); final BoxDefinition boxDefinition = box.getBoxDefinition(); final long preferredHeight = boxDefinition.getPreferredHeight().resolve(0); final long minHeight = boxDefinition.getMinimumHeight().resolve(0); final long maxHeight = boxDefinition.getMaximumHeight().resolve(0, MAX_AUTO); final long preferredSize = ProcessUtility.computeLength(minHeight, maxHeight, preferredHeight); currentTable.updateDefinedSize(rowSpan, preferredSize); return true; } }
public void testWeirdTocLayout() throws ReportProcessingException, ContentProcessingException { Element textField = new Element(); textField.setName("textField"); textField.getStyle().setStyleProperty(TextStyleKeys.FONT, "Arial"); textField.getStyle().setStyleProperty(TextStyleKeys.FONTSIZE, 14); textField.getStyle().setStyleProperty(TextStyleKeys.TEXT_WRAP, TextWrap.NONE); textField.getStyle().setStyleProperty(ElementStyleKeys.MIN_WIDTH, 97f); textField.getStyle().setStyleProperty(ElementStyleKeys.MIN_HEIGHT, 20f); textField.getStyle().setStyleProperty(ElementStyleKeys.POS_X, 0f); textField.getStyle().setStyleProperty(ElementStyleKeys.POS_Y, 0f); textField.setElementType(LabelType.INSTANCE); textField.setAttribute( AttributeNames.Core.NAMESPACE, AttributeNames.Core.VALUE, "Classic Cars"); Element dotField = new Element(); dotField.setName("dotField"); dotField.getStyle().setStyleProperty(TextStyleKeys.FONT, "Arial"); dotField.getStyle().setStyleProperty(TextStyleKeys.FONTSIZE, 14); dotField.getStyle().setStyleProperty(ElementStyleKeys.ALIGNMENT, ElementAlignment.RIGHT); dotField.getStyle().setStyleProperty(ElementStyleKeys.VALIGNMENT, ElementAlignment.TOP); dotField.getStyle().setStyleProperty(ElementStyleKeys.POS_X, 97f); dotField.getStyle().setStyleProperty(ElementStyleKeys.POS_Y, 0f); dotField.getStyle().setStyleProperty(ElementStyleKeys.MIN_WIDTH, 628.463f); dotField.getStyle().setStyleProperty(ElementStyleKeys.MIN_WIDTH, 20f); dotField.getStyle().setStyleProperty(ElementStyleKeys.WIDTH, 100f); dotField.getStyle().setStyleProperty(ElementStyleKeys.MAX_WIDTH, 100f); dotField.setElementType(LabelType.INSTANCE); dotField.setAttribute( AttributeNames.Core.NAMESPACE, AttributeNames.Core.VALUE, " . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " + " . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " + ". . . . . . . . . . . . . . . . . ."); Band band = new Band(); band.setName("outer-box"); band.setLayout("inline"); band.getStyle().setStyleProperty(ElementStyleKeys.BOX_SIZING, BoxSizing.CONTENT_BOX); band.getStyle().setStyleProperty(ElementStyleKeys.OVERFLOW_X, false); band.getStyle().setStyleProperty(ElementStyleKeys.OVERFLOW_Y, false); band.getStyle().setStyleProperty(TextStyleKeys.LINEHEIGHT, 1f); band.getStyle() .setStyleProperty(TextStyleKeys.WHITE_SPACE_COLLAPSE, WhitespaceCollapse.PRESERVE_BREAKS); band.getStyle().setStyleProperty(ElementStyleKeys.MIN_WIDTH, 708f); band.getStyle().setStyleProperty(ElementStyleKeys.MIN_HEIGHT, 12f); band.getStyle().setStyleProperty(ElementStyleKeys.MAX_HEIGHT, 20f); band.addElement(textField); band.addElement(dotField); final MasterReport report = new MasterReport(); report.getReportHeader().addElement(band); LogicalPageBox logicalPageBox = DebugReportRunner.layoutSingleBand(report, report.getReportHeader(), false, false); // ModelPrinter.INSTANCE.print(logicalPageBox); RenderBox outerBox = (RenderBox) MatchFactory.findElementByName(logicalPageBox, "outer-box"); RenderNode dotFieldBox = MatchFactory.findElementByName(logicalPageBox, "dotField"); RenderNode textFieldBox = MatchFactory.findElementByName(logicalPageBox, "textField"); assertNotNull(outerBox); assertNotNull(dotFieldBox); assertNotNull(textFieldBox); assertEquals(0, outerBox.getY()); assertEquals(0, dotFieldBox.getY()); assertEquals(0, textFieldBox.getY()); // box only contains one line, and min-size is set to 8, max size = 20, so the line-height of // 14.024 is used. assertEquals(StrictGeomUtility.toInternalValue(14.024), outerBox.getHeight()); assertEquals(StrictGeomUtility.toInternalValue(14.024), outerBox.getFirstChild().getHeight()); assertEquals(StrictGeomUtility.toInternalValue(14), dotFieldBox.getHeight()); assertEquals(StrictGeomUtility.toInternalValue(14), textFieldBox.getHeight()); }
protected boolean drawImage( final RenderableReplacedContentBox content, final Image image, final com.lowagie.text.Image itextImage) { final StyleSheet layoutContext = content.getStyleSheet(); final boolean shouldScale = layoutContext.getBooleanStyleProperty(ElementStyleKeys.SCALE); final int x = (int) StrictGeomUtility.toExternalValue(content.getX()); final int y = (int) StrictGeomUtility.toExternalValue(content.getY()); final int width = (int) StrictGeomUtility.toExternalValue(content.getWidth()); final int height = (int) StrictGeomUtility.toExternalValue(content.getHeight()); if (width == 0 || height == 0) { PdfLogicalPageDrawable.logger.debug("Error: Image area is empty: " + content); return false; } final WaitingImageObserver obs = new WaitingImageObserver(image); obs.waitImageLoaded(); final int imageWidth = image.getWidth(obs); final int imageHeight = image.getHeight(obs); if (imageWidth < 1 || imageHeight < 1) { return false; } final Rectangle2D.Double drawAreaBounds = new Rectangle2D.Double(x, y, width, height); final AffineTransform scaleTransform; final Graphics2D g2; if (shouldScale == false) { double deviceScaleFactor = 1; final double devResolution = getMetaData().getNumericFeatureValue(OutputProcessorFeature.DEVICE_RESOLUTION); if (getMetaData().isFeatureSupported(OutputProcessorFeature.IMAGE_RESOLUTION_MAPPING)) { if (devResolution != 72.0 && devResolution > 0) { // Need to scale the device to its native resolution before attempting to draw the image.. deviceScaleFactor = (72.0 / devResolution); } } final int clipWidth = Math.min(width, (int) Math.ceil(deviceScaleFactor * imageWidth)); final int clipHeight = Math.min(height, (int) Math.ceil(deviceScaleFactor * imageHeight)); final ElementAlignment horizontalAlignment = (ElementAlignment) layoutContext.getStyleProperty(ElementStyleKeys.ALIGNMENT); final ElementAlignment verticalAlignment = (ElementAlignment) layoutContext.getStyleProperty(ElementStyleKeys.VALIGNMENT); final int alignmentX = (int) RenderUtility.computeHorizontalAlignment(horizontalAlignment, width, clipWidth); final int alignmentY = (int) RenderUtility.computeVerticalAlignment(verticalAlignment, height, clipHeight); g2 = (Graphics2D) getGraphics().create(); g2.clip(drawAreaBounds); g2.translate(x, y); g2.translate(alignmentX, alignmentY); g2.clip(new Rectangle2D.Float(0, 0, clipWidth, clipHeight)); g2.scale(deviceScaleFactor, deviceScaleFactor); scaleTransform = null; } else { g2 = (Graphics2D) getGraphics().create(); g2.clip(drawAreaBounds); g2.translate(x, y); g2.clip(new Rectangle2D.Float(0, 0, width, height)); final double scaleX; final double scaleY; final boolean keepAspectRatio = layoutContext.getBooleanStyleProperty(ElementStyleKeys.KEEP_ASPECT_RATIO); if (keepAspectRatio) { final double scaleFactor = Math.min(width / (double) imageWidth, height / (double) imageHeight); scaleX = scaleFactor; scaleY = scaleFactor; } else { scaleX = width / (double) imageWidth; scaleY = height / (double) imageHeight; } final int clipWidth = (int) (scaleX * imageWidth); final int clipHeight = (int) (scaleY * imageHeight); final ElementAlignment horizontalAlignment = (ElementAlignment) layoutContext.getStyleProperty(ElementStyleKeys.ALIGNMENT); final ElementAlignment verticalAlignment = (ElementAlignment) layoutContext.getStyleProperty(ElementStyleKeys.VALIGNMENT); final int alignmentX = (int) RenderUtility.computeHorizontalAlignment(horizontalAlignment, width, clipWidth); final int alignmentY = (int) RenderUtility.computeVerticalAlignment(verticalAlignment, height, clipHeight); g2.translate(alignmentX, alignmentY); scaleTransform = AffineTransform.getScaleInstance(scaleX, scaleY); } final PdfGraphics2D pdfGraphics2D = (PdfGraphics2D) g2; pdfGraphics2D.drawPdfImage(itextImage, image, scaleTransform, null); g2.dispose(); return true; }
/** @noinspection IOResourceOpenedButNotSafelyClosed */ public void print( final LogicalPageKey logicalPageKey, final LogicalPageBox logicalPage, final TableContentProducer contentProducer, final RTFOutputProcessorMetaData metaData, final boolean incremental) throws ContentProcessingException { final int startRow = contentProducer.getFinishedRows(); final int finishRow = contentProducer.getFilledRows(); if (incremental && startRow == finishRow) { return; } if (document == null) { this.cellBackgroundProducer = new CellBackgroundProducer( metaData.isFeatureSupported(AbstractTableOutputProcessor.TREAT_ELLIPSE_AS_RECTANGLE), metaData.isFeatureSupported(OutputProcessorFeature.UNALIGNED_PAGEBANDS)); final PhysicalPageBox pageFormat = logicalPage.getPageGrid().getPage(0, 0); final float urx = (float) StrictGeomUtility.toExternalValue(pageFormat.getWidth()); final float ury = (float) StrictGeomUtility.toExternalValue(pageFormat.getHeight()); final float marginLeft = (float) StrictGeomUtility.toExternalValue(pageFormat.getImageableX()); final float marginRight = (float) StrictGeomUtility.toExternalValue( pageFormat.getWidth() - pageFormat.getImageableWidth() - pageFormat.getImageableX()); final float marginTop = (float) StrictGeomUtility.toExternalValue(pageFormat.getImageableY()); final float marginBottom = (float) StrictGeomUtility.toExternalValue( pageFormat.getHeight() - pageFormat.getImageableHeight() - pageFormat.getImageableY()); final Rectangle pageSize = new Rectangle(urx, ury); document = new Document(pageSize, marginLeft, marginRight, marginTop, marginBottom); imageCache = new RTFImageCache(resourceManager); // rtf does not support PageFormats or other meta data... final PatchRtfWriter2 instance = PatchRtfWriter2.getInstance(document, new NoCloseOutputStream(outputStream)); instance.getDocumentSettings().setAlwaysUseUnicode(true); final String author = config.getConfigProperty( "org.pentaho.reporting.engine.classic.core.modules.output.table.rtf.Author"); if (author != null) { document.addAuthor(author); } final String title = config.getConfigProperty( "org.pentaho.reporting.engine.classic.core.modules.output.table.rtf.Title"); if (title != null) { document.addTitle(title); } document.addProducer(); document.addCreator(RTFPrinter.CREATOR); try { document.addCreationDate(); } catch (Exception e) { RTFPrinter.logger.debug("Unable to add creation date. It will have to work without it.", e); } document.open(); } // Start a new page. try { final SheetLayout sheetLayout = contentProducer.getSheetLayout(); final int columnCount = contentProducer.getColumnCount(); if (table == null) { final int rowCount = contentProducer.getRowCount(); table = new Table(columnCount, rowCount); table.setAutoFillEmptyCells(false); table.setWidth(100); // span the full page.. // and finally the content .. final float[] cellWidths = new float[columnCount]; for (int i = 0; i < columnCount; i++) { cellWidths[i] = (float) StrictGeomUtility.toExternalValue(sheetLayout.getCellWidth(i, i + 1)); } table.setWidths(cellWidths); } // logger.debug ("Processing: " + startRow + " " + finishRow + " " + incremental); for (int row = startRow; row < finishRow; row++) { for (short col = 0; col < columnCount; col++) { final RenderBox content = contentProducer.getContent(row, col); final CellMarker.SectionType sectionType = contentProducer.getSectionType(row, col); if (content == null) { final RenderBox backgroundBox = contentProducer.getBackground(row, col); final CellBackground background; if (backgroundBox != null) { background = cellBackgroundProducer.getBackgroundForBox( logicalPage, sheetLayout, col, row, 1, 1, true, sectionType, backgroundBox); } else { background = cellBackgroundProducer.getBackgroundAt( logicalPage, sheetLayout, col, row, true, sectionType); } if (background == null) { // An empty cell .. ignore final PatchRtfCell cell = new PatchRtfCell(); cell.setBorderWidth(0); cell.setMinimumHeight( (float) StrictGeomUtility.toExternalValue(sheetLayout.getRowHeight(row))); table.addCell(cell, row, col); continue; } // A empty cell with a defined background .. final PatchRtfCell cell = new PatchRtfCell(); cell.setBorderWidth(0); cell.setMinimumHeight( (float) StrictGeomUtility.toExternalValue(sheetLayout.getRowHeight(row))); updateCellStyle(cell, background); table.addCell(cell, row, col); continue; } if (content.isCommited() == false) { throw new InvalidReportStateException("Uncommited content encountered"); } final long contentOffset = contentProducer.getContentOffset(row, col); final long colPos = sheetLayout.getXPosition(col); final long rowPos = sheetLayout.getYPosition(row); if (content.getX() != colPos || (content.getY() + contentOffset) != rowPos) { // A spanned cell .. continue; } final int colSpan = sheetLayout.getColSpan(col, content.getX() + content.getWidth()); final int rowSpan = sheetLayout.getRowSpan(row, content.getY() + content.getHeight() + contentOffset); final CellBackground realBackground = cellBackgroundProducer.getBackgroundForBox( logicalPage, sheetLayout, col, row, colSpan, rowSpan, false, sectionType, content); final PatchRtfCell cell = new PatchRtfCell(); cell.setRowspan(rowSpan); cell.setColspan(colSpan); cell.setBorderWidth(0); cell.setMinimumHeight( (float) StrictGeomUtility.toExternalValue(sheetLayout.getRowHeight(row))); if (realBackground != null) { updateCellStyle(cell, realBackground); } computeCellStyle(content, cell); // export the cell and all content .. final RTFTextExtractor etx = new RTFTextExtractor(metaData); etx.compute(content, cell, imageCache); table.addCell(cell, row, col); content.setFinishedTable(true); // logger.debug("set Finished to cell (" + col + ", " + row + "," + content.getName() + // ")"); } } if (incremental == false) { document.add(table); table = null; } } catch (DocumentException e) { throw new ContentProcessingException("Failed to generate RTF-Document", e); } }
protected void drawText(final RenderableText renderableText, final long contentX2) { if (renderableText.getLength() == 0) { return; } final long posX = renderableText.getX(); final long posY = renderableText.getY(); final float x1 = (float) (StrictGeomUtility.toExternalValue(posX)); final PdfContentByte cb; PdfTextSpec textSpec = (PdfTextSpec) getTextSpec(); if (textSpec == null) { final StyleSheet layoutContext = renderableText.getStyleSheet(); // The code below may be weird, but at least it is predictable weird. final String fontName = getMetaData() .getNormalizedFontFamilyName( (String) layoutContext.getStyleProperty(TextStyleKeys.FONT)); final String encoding = (String) layoutContext.getStyleProperty(TextStyleKeys.FONTENCODING); final float fontSize = (float) layoutContext.getDoubleStyleProperty(TextStyleKeys.FONTSIZE, 10); final boolean embed = globalEmbed || layoutContext.getBooleanStyleProperty(TextStyleKeys.EMBEDDED_FONT); final boolean bold = layoutContext.getBooleanStyleProperty(TextStyleKeys.BOLD); final boolean italics = layoutContext.getBooleanStyleProperty(TextStyleKeys.ITALIC); final BaseFontFontMetrics fontMetrics = getMetaData() .getBaseFontFontMetrics(fontName, fontSize, bold, italics, encoding, embed, false); final PdfGraphics2D g2 = (PdfGraphics2D) getGraphics(); final Color cssColor = (Color) layoutContext.getStyleProperty(ElementStyleKeys.PAINT); g2.setPaint(cssColor); g2.setFillPaint(); g2.setStrokePaint(); // final float translateY = (float) affineTransform.getTranslateY(); cb = g2.getRawContentByte(); textSpec = new PdfTextSpec(layoutContext, getMetaData(), g2, fontMetrics, cb); setTextSpec(textSpec); cb.beginText(); cb.setFontAndSize(fontMetrics.getBaseFont(), fontSize); } else { cb = textSpec.getContentByte(); } final BaseFontFontMetrics baseFontRecord = textSpec.getFontMetrics(); final BaseFont baseFont = baseFontRecord.getBaseFont(); final float ascent = baseFont.getFontDescriptor(BaseFont.BBOXURY, textSpec.getFontSize()); final float y2 = (float) (StrictGeomUtility.toExternalValue(posY) + ascent); final float y = globalHeight - y2; final AffineTransform affineTransform = textSpec.getGraphics().getTransform(); final float translateX = (float) affineTransform.getTranslateX(); final FontNativeContext nativeContext = baseFontRecord.getNativeContext(); if (baseFontRecord.isTrueTypeFont() && textSpec.isBold() && nativeContext.isNativeBold() == false) { final float strokeWidth = textSpec.getFontSize() / 30.0f; // right from iText ... if (strokeWidth == 1) { cb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL); } else { cb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE); cb.setLineWidth(strokeWidth); } } else { cb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL); } // if the font does not declare to be italics already, emulate it .. if (baseFontRecord.isTrueTypeFont() && textSpec.isItalics() && nativeContext.isNativeItalics() == false) { final float italicAngle = baseFont.getFontDescriptor(BaseFont.ITALICANGLE, textSpec.getFontSize()); if (italicAngle == 0) { // italics requested, but the font itself does not supply italics gylphs. cb.setTextMatrix(1, 0, PdfLogicalPageDrawable.ITALIC_ANGLE, 1, x1 + translateX, y); } else { cb.setTextMatrix(x1 + translateX, y); } } else { cb.setTextMatrix(x1 + translateX, y); } final OutputProcessorMetaData metaData = getMetaData(); final GlyphList gs = renderableText.getGlyphs(); final int offset = renderableText.getOffset(); final CodePointBuffer codePointBuffer = getCodePointBuffer(); if (metaData.isFeatureSupported(OutputProcessorFeature.FAST_FONTRENDERING) && isNormalTextSpacing(renderableText)) { final int maxLength = renderableText.computeMaximumTextSize(contentX2); final String text = gs.getText(renderableText.getOffset(), maxLength, codePointBuffer); cb.showText(text); } else { final PdfTextArray textArray = new PdfTextArray(); final StringBuilder buffer = new StringBuilder(gs.getSize()); final int maxPos = offset + renderableText.computeMaximumTextSize(contentX2); for (int i = offset; i < maxPos; i++) { final Glyph g = gs.getGlyph(i); final Spacing spacing = g.getSpacing(); if (i != offset) { final float optimum = (float) StrictGeomUtility.toFontMetricsValue(spacing.getMinimum()); if (optimum != 0) { textArray.add(buffer.toString()); textArray.add(-optimum / textSpec.getFontSize()); buffer.setLength(0); } } final String text = gs.getGlyphAsString(i, codePointBuffer); buffer.append(text); } if (buffer.length() > 0) { textArray.add(buffer.toString()); } cb.showText(textArray); } }
static { final long value = StrictGeomUtility.toInternalValue(1); conversionFactor = value / org.pentaho.reporting.libraries.fonts.tools.StrictGeomUtility.toInternalValue(1); }
public void update(final Point2D normalizedPoint, final double zoomFactor) { final SnapPositionsModel horizontalSnapModel = getHorizontalSnapModel(); final Element[] selectedVisualElements = getSelectedVisualElements(); final long originPointX = getOriginPointX(); final long[] elementWidth = getElementWidth(); final long px = StrictGeomUtility.toInternalValue(normalizedPoint.getX()); final long dx = px - originPointX; for (int i = 0; i < selectedVisualElements.length; i++) { final Element element = selectedVisualElements[i]; if (element instanceof RootLevelBand) { continue; } final ElementStyleSheet styleSheet = element.getStyle(); final double elementMinWidth = styleSheet.getDoubleStyleProperty(ElementStyleKeys.MIN_WIDTH, 0); // this is where I want the element on a global scale... final long targetWidth = elementWidth[i] + dx; final CachedLayoutData data = ModelUtility.getCachedLayoutData(element); final long elementX = data.getX(); final long targetX2 = elementX + targetWidth; if (elementMinWidth >= 0) { // absolute position; resolving is easy here final long snapPosition = horizontalSnapModel.getNearestSnapPosition(targetX2, element.getObjectID()); if (Math.abs(snapPosition - targetX2) > snapThreshold) { final long localWidth = Math.max(0, targetX2 - elementX); final float position = (float) StrictGeomUtility.toExternalValue(localWidth); styleSheet.setStyleProperty(ElementStyleKeys.MIN_WIDTH, new Float(position)); } else { final long localWidth = Math.max(0, snapPosition - elementX); final float position = (float) StrictGeomUtility.toExternalValue(localWidth); styleSheet.setStyleProperty(ElementStyleKeys.MIN_WIDTH, new Float(position)); } } else { final Element parent = element.getParentSection(); final CachedLayoutData parentData = ModelUtility.getCachedLayoutData(parent); final long parentBase = parentData.getWidth(); if (parentBase > 0) { // relative position; resolve the percentage against the height of the parent. final long snapPosition = horizontalSnapModel.getNearestSnapPosition(targetX2, element.getObjectID()); if (Math.abs(snapPosition - targetX2) > snapThreshold) { final long localWidth = Math.max(0, targetX2 - elementX); // strict geometry: all values are multiplied by 1000 // percentages in the engine are represented by floats betwen 0 and 100. final long percentage = StrictGeomUtility.toInternalValue(localWidth * 100 / parentBase); styleSheet.setStyleProperty( ElementStyleKeys.MIN_WIDTH, new Float(StrictGeomUtility.toExternalValue(-percentage))); } else { final long localWidth = Math.max(0, snapPosition - elementX); // strict geometry: all values are multiplied by 1000 // percentages in the engine are represented by floats betwen 0 and 100. final long percentage = StrictGeomUtility.toInternalValue(localWidth * 100 / parentBase); styleSheet.setStyleProperty( ElementStyleKeys.MIN_WIDTH, new Float(StrictGeomUtility.toExternalValue(-percentage))); } } } element.notifyNodePropertiesChanged(); } }
public void alignLeft() { final int theShiftLeft = (int) (0 - StrictGeomUtility.toExternalValue(computeFarLeftPosition())); align(theShiftLeft, visualElements); registerChanges(); }