Complex header repeating on every PDF page - itext

I am using iText7 to generate PDFs. I have to repeat a bit complex header on every page.
My complex header is three paragraphs with different formatting of each one, centered, something like this:
I tried to mimic an example with repeating Table as a header https://kb.itextpdf.com/home/it7kb/faq/how-to-add-a-table-as-a-header without success.
If I follow an example and replace Table with a Div created as (pseudo-code)
Paragraph myTitle = new Paragraph();
Paragraph title1, subtitle2, sub_subtitle3; // All initialised properly
myTitle.add(title1).add(new AreaBreak(NEXT_AREA)).add(subtitle2).add(sub_subtitle3);
Title appears, but all Title/Subtitle/Sub-subtitle are in one line. How can I insert line breaks between the paragraphs?
If I follow an example and replace Table with a Div created as (pseudo-code) Div.add(title1).add(new AreaBreak(NEXT_AREA)).add(subtitle2).add(sub_subtitle3);
nothing appears at all as a title.
Any ideas of how to achieve the desired effect?

I was able to solve the issue. It is fairly close to the idea explained on the IText knowledge base in the Table as a header example. The key is to update canvas position after each paragraph of the title is added:
canvas.add(titleElement.e).setFixedPosition(doc.getLeftMargin(), titleElement.h + doc.getTopMargin(), width);
Full code below is based on the TableHeader example from here.
In my example complex title is a list of Paragraph, but can be any list if IBlockElement
I've used itext 7.1.13
package samples;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.AreaBreak;
import com.itextpdf.layout.element.IBlockElement;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.layout.LayoutArea;
import com.itextpdf.layout.layout.LayoutContext;
import com.itextpdf.layout.layout.LayoutResult;
import com.itextpdf.layout.property.TextAlignment;
import com.itextpdf.layout.renderer.DocumentRenderer;
import com.itextpdf.layout.renderer.ParagraphRenderer;
import java.io.File;
import java.util.List;
import java.util.stream.Collectors;
public class ComplexHeader {
public static final String DEST = "./target/sandbox/events/table_header.pdf";
private static final PageSize pageSize = PageSize.A4;
public static void main(String[] args) throws Exception {
File file = new File(DEST);
file.getParentFile().mkdirs();
new ComplexHeader().manipulatePdf(DEST);
}
protected void manipulatePdf(String dest) throws Exception {
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
Document doc = new Document(pdfDoc, pageSize);
String loremIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
List<IBlockElement> title = List.of(
titleWithProp("Title", 14, 5, 3, false),
titleWithProp("Subtitle", 12, 3, 2, true),
titleWithProp("Sub-Subtitle", 7, 0, 2, false));
ComplexHeaderEventHandler handler = new ComplexHeaderEventHandler(doc, title);
pdfDoc.addEventHandler(PdfDocumentEvent.END_PAGE, handler);
// Calculate top margin to be sure that the table will fit the margin.
float topMargin = 20 + handler.getTitleHeight();
doc.setMargins(topMargin, 36, 36, 36);
for (int i = 0; i < 10; i++) {
doc.add(new Paragraph(loremIpsum));
}
doc.add(new AreaBreak());
doc.add(new Paragraph(loremIpsum));
doc.add(new AreaBreak());
doc.add(new Paragraph(loremIpsum));
doc.close();
}
private static Paragraph titleWithProp(String text, float fontSize,
float topMargin, float bottomMargine, boolean isBold) {
Paragraph p = new Paragraph(text).setTextAlignment(TextAlignment.CENTER)
.setFontSize(fontSize).setMarginTop(topMargin).setMarginBottom(bottomMargine);
if (isBold) {
p.setBold();
}
return p;
}
private static class ComplexHeaderEventHandler implements IEventHandler {
private float titleHeight;
private Document doc;
private List<TitleComponentHolder> documentTitle;
private static class TitleComponentHolder {
IBlockElement e;
public float getH() {
return h;
}
float h;
public TitleComponentHolder(IBlockElement e, float h) {
this.e = e;
this.h = h;
}
}
public ComplexHeaderEventHandler(Document doc, List<IBlockElement> newTitle) {
this.doc = doc;
documentTitle = newTitle.stream().map(t -> new TitleComponentHolder(t, calculateElementHeight(t))).
collect(Collectors.toList());
titleHeight = documentTitle.stream().map(TitleComponentHolder::getH).reduce(0f, Float::sum);
}
#Override
public void handleEvent(Event currentEvent) {
PdfDocumentEvent docEvent = (PdfDocumentEvent) currentEvent;
PdfDocument pdfDoc = docEvent.getDocument();
PdfPage page = docEvent.getPage();
PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), pdfDoc);
float coordX = pageSize.getX() + doc.getLeftMargin();
float coordY = pageSize.getTop() - doc.getTopMargin();
float width = pageSize.getWidth() - doc.getRightMargin() - doc.getLeftMargin();
Rectangle rect = new Rectangle(coordX, coordY, width, titleHeight);
try (Canvas canvas = new Canvas(pdfCanvas, rect)) {
documentTitle.forEach(title ->
canvas.add(title.e).setFixedPosition(doc.getLeftMargin(), title.h + doc.getTopMargin(), width));
}
}
public float getTitleHeight() {
return titleHeight;
}
private float calculateElementHeight(IBlockElement e) {
ParagraphRenderer renderer = (ParagraphRenderer) e.createRendererSubTree();
renderer.setParent(new DocumentRenderer(doc));
// Simulate the positioning of the renderer to find out how much space the header table will occupy.
LayoutResult result = renderer.layout(new LayoutContext(new LayoutArea(0, pageSize)));
return result.getOccupiedArea().getBBox().getHeight();
}
}
}

Related

How can I open a canvas by clicking on an object in Unity

As you can see in the first image, I have a store, which has many objects. My intention is to click on an object and a Canvas with the object's information is displayed, but I don't know how to do it.
I created a canvas example (as you can see in the second image) that I open by pressing the "space", but I want to open it by clicking on a vase (gameobject), any ideas?
Greetings.
Images:
Objects
Canvas piece information
Step 1:
Get a reference to your canvas
[SerializeField] private GameObject canvas;
Step 2:
Enable canvas onClick
void TaskOnClick() {
canvas.SetActive(true);
}
Make sure that the canvas is disabled by default.
Add IPointerClickHandler interface to your script and use the method OnPointerClick to call a method that turns on your canvas GameObject. You will need a reference for your canvas, you can set it in the inspector after adding defining it in the script as shown in the code below. (You need to attach the script to the item game object)
public class ItemInfo : MonoBehaviour, IPointerClickHandler
{
[SerializeField] private GameObject itemInfoCanvas;
public void ShowItemInfo()
{
itemInfoCanvas.SetActive(true);
}
public void OnPointerClick(PointerEventData eventData)
{
if (eventData.button == PointerEventData.InputButton.Right)
ShowItemInfo();
}
}
Another way to achieve it with the old input system:
public class ItemInfo : MonoBehaviour
{
[SerializeField] private GameObject itemInfoCanvas;
public void ShowItemInfo()
{
itemInfoCanvas.SetActive(true);
}
private void Update()
{
if (Input.GetMouseButtonUp(0)) //Right click, triggered after releasing.
ShowItemInfo();
}
}
Same way as above with the new input system:
private void Update()
{
if (!Mouse.current.leftButton.wasReleasedThisFrame)
{
ShowItemInfo();
}
Read more about the pointer click handlers here
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class CameraInteraction : MonoBehaviour
{
private new Transform camera;
private float rayDistance = 2;
public Canvas PieceInformationCanvas;
public Canvas MinimapCanvas;
public Text titleText, descriptionText;
private GameObject test;
void Start()
{
camera = transform.Find("Camera");
PieceInformationCanvas.enabled = false;
test = PieceInformationCanvas.transform.GetChild(2).gameObject;
test.SetActive(false);
}
void Update()
{
// Parametros:
// 1) Desde donde iniciara el rayo
// 2) Hacia donde se dirige el rayo (hacia adelante(eje x))
// 3) Color del rayo
Debug.DrawRay(camera.position, camera.forward * rayDistance, Color.red);
if (Input.GetButtonDown("Interactable"))
{
// RaycastHit hit contiene la informacion del objeto que estamos mirando
RaycastHit hit;
if (Physics.Raycast(camera.position, camera.forward, out hit, rayDistance, LayerMask.GetMask("Interactable")))
{
hit.transform.GetComponent<Interactable>().Interact();
ShowPieceCanvas();
}
}
}
public void ShowPieceCanvas()
{
string title = "Titulo Pieza Artesanal";
string description = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
titleText.text = title;
descriptionText.text = description;
test.SetActive(true);
Cursor.lockState = CursorLockMode.None;
PieceInformationCanvas.enabled = true;
MinimapCanvas.enabled = false;
}
public void HidePieceCanvas()
{
test.SetActive(false);
PieceInformationCanvas.enabled = false;
Cursor.lockState = CursorLockMode.Locked;
MinimapCanvas.enabled = true;
}
}

itextsharp change margins no document.SetPageSize

I added the function below to change the margins for the page
at every page change.
I found the forum a method that sets the size of the page:
document.SetPageSize (New Rectangle (36.0F, 36.0F, 52.0F, PageFooter.TotalHeight))
But I do not want to change the size of the page, but those margins.
thanks
public override void OnEndPage(PdfWriter writer, Document document)
{
try
{
DataSet dsReport = new DataSet();
foreach (DataSet obj in report.arrayDs)
{
dsReport = obj;
break;
}
Single topMargin = 0;
if (document.PageNumber != 1)
{
if (report.repeatHead) //ripete l'intestazione del report su tutte le pagine di stampa
{
repeatHead(writer, document);
topMargin = 60;
}
else
{
if (document.PageNumber == 2) //ripete l'intestazione del report solo sulla second pagina dopo la copertina
{
repeatHead(writer, document);
topMargin = 60;
}
else
{
topMargin = Convert.ToSingle(dsReport.Tables["REPORT_STYLE"].Rows[0]["topMargin"]) * 10;
}
}
document.SetMargins(Convert.ToSingle(dsReport.Tables["REPORT_STYLE"].Rows[0]["leftMargin"]) * 10,
Convert.ToSingle(dsReport.Tables["REPORT_STYLE"].Rows[0]["rightMargin"]) * 10,
topMargin,
Convert.ToSingle(dsReport.Tables["REPORT_STYLE"].Rows[0]["bottomMargin"]) * 10);
}
}
catch
{ throw; }
}
Based on your clarification in the comments, you want the top margin of the first page to be 60 and the top margin of the second page to be 0.
This is shown in the following screen shot:
The Java code to achieve this looks like this:
public void createPdf(String dest) throws IOException, DocumentException {
float left = 30;
float right = 30;
float top = 60;
float bottom = 0;
Document document = new Document(PageSize.A4, left, right, top, bottom);
PdfWriter.getInstance(document, new FileOutputStream(dest));
document.open();
document.setMargins(left, right, 0, bottom);
for (int i = 0; i < 60; i++) {
document.add(new Paragraph("This is a test"));
}
document.close();
}
If you want to port this to C#, you need to change some lower cases into upper cases. You already knew most of the methods, for instance: I see document.SetMargins(...) in your page event.

How can you eliminate white-space in multiple columns using iTextSharp?

I'd like to add a Paragraph of text to pages in 2 columns. I understand that MultiColumnText has been eliminated. I know I can create column 1, write to it, and if there is more text create column 2 and write to it. If there is still more text, go to the next page and repeat.
However I always end up with either:
a long chunk of text orphaned in the left column.
a full left column and partially used right column.
How can I format my content in 2 columns while reducing white space, such as compressing the columns so I end with 2 full columns of equal length?
Thanks!
You should add your column twice, once in simulation mode and once for real.
I have adapted the ColumnTextParagraphs example to show what is meant by simulation mode. Take a look at the ColumnTextParagraphs2 example:
We add the column in simulation mode to obtain the total height needed for the column. This is done in the following method:
public float getNecessaryHeight(ColumnText ct) throws DocumentException {
ct.setSimpleColumn(new Rectangle(0, 0, COLUMN_WIDTH, -500000));
ct.go(true);
return -ct.getYLine();
}
We use this height when we add the left and right column:
Rectangle left;
float top = COLUMNS[0].getTop();
float middle = (COLUMNS[0].getLeft() + COLUMNS[1].getRight()) / 2;
float columnheight;
int status = ColumnText.START_COLUMN;
while (ColumnText.hasMoreText(status)) {
if (checkHeight(height)) {
columnheight = COLUMNS[0].getHeight();
left = COLUMNS[0];
}
else {
columnheight = (height / 2) + ERROR_MARGIN;
left = new Rectangle(
COLUMNS[0].getLeft(),
COLUMNS[0].getTop() - columnheight,
COLUMNS[0].getRight(),
COLUMNS[0].getTop()
);
}
// left half
ct.setSimpleColumn(left);
ct.go();
height -= COLUMNS[0].getTop() - ct.getYLine();
// separator
canvas.moveTo(middle, top - columnheight);
canvas.lineTo(middle, top);
canvas.stroke();
// right half
ct.setSimpleColumn(COLUMNS[1]);
status = ct.go();
height -= COLUMNS[1].getTop() - ct.getYLine();
// new page
document.newPage();
}
This is how we check the height:
public boolean checkHeight(float height) {
height -= COLUMNS[0].getHeight() + COLUMNS[1].getHeight() + ERROR_MARGIN;
return height > 0;
}
As you can see, we add the full columns as long as the height of both columns is smaller than the remaining height. When columns are added, we adjust the remaining height. As soon as the height is lower than the height of to columns, we adapt the height of the first column.
Note that we work with an ERROR_MARGIN because dividing by two often leads to a situation where the second column has one line more than the first column. It is better when it's the other way around.
This is the full example at your request:
/**
* Example written by Bruno Lowagie in answer to:
* http://stackoverflow.com/questions/29378407/how-can-you-eliminate-white-space-in-multiple-columns-using-itextsharp
*/
package sandbox.objects;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfWriter;
public class ColumnTextParagraphs2 {
public static final String DEST = "results/objects/column_paragraphs2.pdf";
public static final String TEXT = "This is some long paragraph that will be added over and over again to prove a point.";
public static final float COLUMN_WIDTH = 254;
public static final float ERROR_MARGIN = 16;
public static final Rectangle[] COLUMNS = {
new Rectangle(36, 36, 36 + COLUMN_WIDTH, 806),
new Rectangle(305, 36, 305 + COLUMN_WIDTH, 806)
};
public static void main(String[] args) throws IOException, DocumentException {
File file = new File(DEST);
file.getParentFile().mkdirs();
new ColumnTextParagraphs2().createPdf(DEST);
}
public void createPdf(String dest) throws IOException, DocumentException {
// step 1
Document document = new Document();
// step 2
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest));
// step 3
document.open();
// step 4
PdfContentByte canvas = writer.getDirectContent();
ColumnText ct = new ColumnText(canvas);
addContent(ct);
float height = getNecessaryHeight(ct);
addContent(ct);
Rectangle left;
float top = COLUMNS[0].getTop();
float middle = (COLUMNS[0].getLeft() + COLUMNS[1].getRight()) / 2;
float columnheight;
int status = ColumnText.START_COLUMN;
while (ColumnText.hasMoreText(status)) {
if (checkHeight(height)) {
columnheight = COLUMNS[0].getHeight();
left = COLUMNS[0];
}
else {
columnheight = (height / 2) + ERROR_MARGIN;
left = new Rectangle(
COLUMNS[0].getLeft(),
COLUMNS[0].getTop() - columnheight,
COLUMNS[0].getRight(),
COLUMNS[0].getTop()
);
}
// left half
ct.setSimpleColumn(left);
ct.go();
height -= COLUMNS[0].getTop() - ct.getYLine();
// separator
canvas.moveTo(middle, top - columnheight);
canvas.lineTo(middle, top);
canvas.stroke();
// right half
ct.setSimpleColumn(COLUMNS[1]);
status = ct.go();
height -= COLUMNS[1].getTop() - ct.getYLine();
// new page
document.newPage();
}
// step 5
document.close();
}
public void addContent(ColumnText ct) {
for (int i = 0; i < 35; i++) {
ct.addElement(new Paragraph(String.format("Paragraph %s: %s", i, TEXT)));
}
}
public float getNecessaryHeight(ColumnText ct) throws DocumentException {
ct.setSimpleColumn(new Rectangle(0, 0, COLUMN_WIDTH, -500000));
ct.go(true);
return -ct.getYLine();
}
public boolean checkHeight(float height) {
height -= COLUMNS[0].getHeight() + COLUMNS[1].getHeight() + ERROR_MARGIN;
return height > 0;
}
}

using JavaFX, how can I place one shape on top of the other?

I am trying to make a checkers game. I will have 64 rectangles, and some of the rectangles will have circles on top of them (representing a checkers piece). How can I do this (i.e. place one shape on top of the other)?
If it matters I'm using Scala, not Java (though the syntax is similar, at least for this part of the project...)
Wrap your shapes in another node; StackPane seems ideal for this. Set its alignment to Pos. CENTER to achieve the effect you want.
Bear in mind, this is not an entirely efficient solution as you will be creating 64 StackPanes. If you want to avoid that, then you would need to do some math to manually position your pieces above the desired game board location. This, however, should not be entirely difficult as you know the dimensions of your board squares, pieces, and how they are laid out.
CAG Gonzo's answer will work, but also be aware that if you place multiple nodes in the same cell in a grid pane, they will be placed on top of each other. This might work pretty well for you application:
import javafx.application.Application;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.RowConstraints;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class CheckerBoard extends Application {
private static final int BOARD_SIZE = 8 ;
private static final int SQUARE_SIZE= 40 ;
private static final int NUM_PIECES = 12 ;
#Override
public void start(Stage primaryStage) {
GridPane checkerBoard = new GridPane();
configureBoardLayout(checkerBoard);
addSquaresToBoard(checkerBoard);
Circle[] whitePieces = new Circle[NUM_PIECES];
Circle[] blackPieces = new Circle[NUM_PIECES];
addPiecesToBoard(checkerBoard, whitePieces, blackPieces);
BorderPane root = new BorderPane(checkerBoard);
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
private void addPiecesToBoard(GridPane checkerBoard, Circle[] whitePieces,
Circle[] blackPieces) {
for (int i=0; i<NUM_PIECES; i++) {
whitePieces[i] = new Circle(SQUARE_SIZE/2-4, Color.WHITE);
whitePieces[i].setStroke(Color.BLACK);
checkerBoard.add(whitePieces[i], i%(BOARD_SIZE/2) * 2 + (2*i/BOARD_SIZE)%2, BOARD_SIZE - 1 - (i*2)/BOARD_SIZE);
blackPieces[i] = new Circle(SQUARE_SIZE/2 -4, Color.BLACK);
blackPieces[i].setStroke(Color.WHITE);
checkerBoard.add(blackPieces[i], i%(BOARD_SIZE/2) * 2 + (1 + 2*i/BOARD_SIZE)%2, (i*2)/BOARD_SIZE);
}
}
private void addSquaresToBoard(GridPane board) {
Color[] squareColors = new Color[] {Color.WHITE, Color.BLACK};
for (int row = 0; row < BOARD_SIZE; row++) {
for (int col = 0; col < BOARD_SIZE; col++) {
board.add(new Rectangle(SQUARE_SIZE, SQUARE_SIZE, squareColors[(row+col)%2]), col, row);
}
}
}
private void configureBoardLayout(GridPane board) {
for (int i=0; i<BOARD_SIZE; i++) {
RowConstraints rowConstraints = new RowConstraints();
rowConstraints.setMinHeight(SQUARE_SIZE);
rowConstraints.setPrefHeight(SQUARE_SIZE);
rowConstraints.setMaxHeight(SQUARE_SIZE);
rowConstraints.setValignment(VPos.CENTER);
board.getRowConstraints().add(rowConstraints);
ColumnConstraints colConstraints = new ColumnConstraints();
colConstraints.setMinWidth(SQUARE_SIZE);
colConstraints.setMaxWidth(SQUARE_SIZE);
colConstraints.setPrefWidth(SQUARE_SIZE);
colConstraints.setHalignment(HPos.CENTER);
board.getColumnConstraints().add(colConstraints);
}
}
public static void main(String[] args) {
launch(args);
}
}

JFreechart draw arc on chart

I have 2 questions
1)I am trying to draw an arc on an XYplot using the shape annotation. I used the XYLine annotation to draw a line and I want the arc to start where the line ends. I am having some issues with the parameters.I want the arc to have a height of 17, width 44, and start at the point(3.0, 17) of the plot(this is where the line ends). But the code below does not work. Can someone please tell me what is wrong with the code?
Arc2D.Double arc = new Arc2D.Double(3.0,
16.9,
44.0,
17.04,
180.0,
180.0,
Arc2D.OPEN
);
plot.addAnnotation(new XYShapeAnnotation(arc,
new BasicStroke(2.0f), Color.white));
XYLineAnnotation a1 = new XYLineAnnotation(3.0, 0.0, 3.0,
16.9, new BasicStroke(2.0f), Color.white);
2)How can I draw a similar figure on a polar plot?
Thanks
The critical thing about Arc2D is the bounding rectangle. To make the half-arc H units high, the bounds must be 2 * H units high.
AFAIK, PolarPlot does not support annotations.
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.geom.Arc2D;
import java.util.Random;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartFrame;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.XYLineAnnotation;
import org.jfree.chart.annotations.XYShapeAnnotation;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
/** #see http://stackoverflow.com/questions/6604211 */
public class ArcTest {
private static final Random r = new Random();
private static final double PI = 180d;
private static final int X = 3;
private static final int Y = 0;
private static final int W = 44;
private static final int H = 17;
public static void main(String[] args) {
JFreeChart chart = ChartFactory.createXYLineChart(
"ArcTest", "X", "Y", createDataset(),
PlotOrientation.VERTICAL, true, true, false);
XYPlot plot = chart.getXYPlot();
XYLineAnnotation line = new XYLineAnnotation(
X, Y, X, H, new BasicStroke(2f), Color.blue);
plot.addAnnotation(line);
Arc2D.Double arc = new Arc2D.Double(
X, Y, W, 2 * H, PI, PI, Arc2D.OPEN);
plot.addAnnotation(new XYShapeAnnotation(arc,
new BasicStroke(2.0f), Color.blue));
ChartFrame frame = new ChartFrame("First", chart);
frame.pack();
frame.setVisible(true);
}
private static XYDataset createDataset() {
XYSeriesCollection result = new XYSeriesCollection();
XYSeries series = new XYSeries("ArcTest");
series.add(0, 0);
series.add(W, W);
result.addSeries(series);
return result;
}
}