From 41132d218b5de9e48ab2dfb081f0ef8e50df882b Mon Sep 17 00:00:00 2001 From: Richard Senior Date: Wed, 17 May 2017 20:52:29 +0100 Subject: [PATCH 1/8] Tidy up .gitignore --- .gitignore | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 4cd67ae..0abb127 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,15 @@ -build/** -bin/** -samples/**/*.xml -.gradle/** -.settings +# Gradle +.gradle/ +build/ + +# Eclipse .classpath .project +.settings +bin/ + +# Ignore samples outputs +samples/**/*.dot +samples/**/*.pdf +samples/**/*.png +samples/**/*.xml From 000a5f930343cbcb2290e5692749c2f0954b03a0 Mon Sep 17 00:00:00 2001 From: Richard Senior Date: Wed, 17 May 2017 20:54:57 +0100 Subject: [PATCH 2/8] Add new visitor to create .dot file --- README.md | 7 ++ samples/Beagle-Pup/pup.clg | 7 +- src/main/java/org/flightgear/clgen/CLGen.java | 2 + .../flightgear/clgen/backend/DotVisitor.java | 119 ++++++++++++++++++ 4 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/flightgear/clgen/backend/DotVisitor.java diff --git a/README.md b/README.md index 040acde..be2541a 100644 --- a/README.md +++ b/README.md @@ -200,6 +200,13 @@ The solution is to create a Nasal script file in the aircraft's Nasal directory and add a custom fgcommand to it using `addcommand`. This command can then be used from both the cockpit control and the checklist. +## What is checklists.dot and how can I view it? + +The .dot file is a visualization of the checklists structure and can be +rendered to an image using a tool like Graphviz. + + $ dot -o checklists.png -Tpng checklists.dot + ## Checklist Definition Language The language is designed to feel familiar to Flightgear developers who are diff --git a/samples/Beagle-Pup/pup.clg b/samples/Beagle-Pup/pup.clg index f34a476..b8998ec 100644 --- a/samples/Beagle-Pup/pup.clg +++ b/samples/Beagle-Pup/pup.clg @@ -332,16 +332,15 @@ checklist("Start Engines") { check("Propeller", "CLEAR"); check("Beacon", "ON"); check("Engine", "START"); - check("Carb Heat", "AS REQUIRED"); +} + +checklist("Before Taxi") { check("Throttle", "TICK OVER"); check("Oil Pressure", "CHECK", "(minimum 25psi)"); check("Starter Master", "OFF"); check("Boost Pump", "OFF"); check("Alternator Warning", "OFF"); check("Transponder", "SBY"); -} - -checklist("Before Taxi") { check("Autopilot", "CHECK"); check("Radios", "ON"); check("Suction", "CHECK", "(minimum 3\")"); diff --git a/src/main/java/org/flightgear/clgen/CLGen.java b/src/main/java/org/flightgear/clgen/CLGen.java index 6ac1c6e..77190be 100644 --- a/src/main/java/org/flightgear/clgen/CLGen.java +++ b/src/main/java/org/flightgear/clgen/CLGen.java @@ -28,6 +28,7 @@ import org.flightgear.clgen.CLGenParser.SpecificationContext; import org.flightgear.clgen.ast.AbstractSyntaxTree; import org.flightgear.clgen.ast.Item; +import org.flightgear.clgen.backend.DotVisitor; import org.flightgear.clgen.backend.UsageVisitor; import org.flightgear.clgen.backend.XmlVisitor; import org.flightgear.clgen.listener.ChecklistListener; @@ -127,6 +128,7 @@ private void run() throws IOException, GeneratorException { ast.accept(usageVisitor); warnings += usageVisitor.getNumberOfWarnings(); ast.accept(new XmlVisitor(input.toAbsolutePath().getParent())); + ast.accept(new DotVisitor(input.toAbsolutePath().getParent())); if (warnings > 0) System.out.format( "Generation complete with %d warning%s.\n", diff --git a/src/main/java/org/flightgear/clgen/backend/DotVisitor.java b/src/main/java/org/flightgear/clgen/backend/DotVisitor.java new file mode 100644 index 0000000..4ddd34c --- /dev/null +++ b/src/main/java/org/flightgear/clgen/backend/DotVisitor.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2017 Richard Senior + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.flightgear.clgen.backend; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.flightgear.clgen.GeneratorException; +import org.flightgear.clgen.ast.AbstractSyntaxTree; +import org.flightgear.clgen.ast.Check; +import org.flightgear.clgen.ast.Checklist; + +/** + * Creates a Graphviz DOT representation of the checklists. + *

+ * This can be rendered to an image using: + * $ dot -ochecklists.png -Tpng checklists.dot + * + * @author Richard Senior + */ +public class DotVisitor extends AbstractVisitor { + + private final Path outputDir; + + private final StringBuilder dot = new StringBuilder(); + private final StringBuilder nodes = new StringBuilder(); + private final StringBuilder edges = new StringBuilder(); + + private int index = 0; + private double hue = 0.0; + private double colorCycle; + + public DotVisitor(final Path outputDir) { + this.outputDir = outputDir; + } + + @Override + public void enter(final AbstractSyntaxTree ast) { + colorCycle = 1.0 / ast.getChecklists().size(); + dot.append("digraph G {\n") + .append(" pad=0.5;\n") + .append(" ranksep=0.35;\n") + .append(" node [fontsize=12];\n") + .append(" node [fontcolor=white,fontname=\"helvetica-bold\"];\n") + .append(" node [shape=Mrecord,width=2.5,style=filled];\n"); + } + + @Override + public void exit(final AbstractSyntaxTree ast) { + dot.append(" node [color=\"#404040\",fontcolor=\"#404040\",fontname=\"helvetica\"];\n") + .append(" node [shape=note,width=2.25,style=\"\"];\n"); + + dot.append(nodes.toString()); + dot.append(edges.toString()); + + dot.append("}\n"); + Path path = outputDir.resolve("checklists.dot"); + try { + Files.write(path, dot.toString().getBytes()); + System.out.println(path.toAbsolutePath().normalize().toString()); + } catch (IOException e) { + String message = String.format("Failed to create DOT file at '%s'", path); + throw new GeneratorException(message, e); + } + } + + @Override + public void enter(final Checklist checklist) { + String s = String.format(" node [color=\"%.04f,0.6,0.6\"]; %s;\n", + hue, quote(checklist.getTitle()) + ); + dot.append(s); + edges.append(" " + quote(checklist.getTitle())); + int totalChecks = checklist.getPages() + .parallelStream() + .mapToInt(page -> page.getChecks().size()) + .sum(); + for (int i = 0; i < totalChecks; ++i) + edges.append(" -> ").append(index + i); + edges.append(";\n"); + } + + @Override + public void exit(final Checklist checklist) { + hue += colorCycle; + } + + @Override + public void enter(final Check check) { + StringBuilder sb = new StringBuilder(); + sb.append(String.format(" %d [label=\"%s %s", + index++, check.getItem().getName(), check.getState().getName() + )); + for (String value : check.getAdditionalValues()) + sb.append("\n" + value.replaceAll("[\"]", "\\\\\"")); + sb.append("\"];\n"); + nodes.append(sb.toString()); + } + + private String quote(final String s) { + return String.format("\"%s\"", s); + } + +} From d2a473e253c99fc022fdb16e4d6f5d7a30aaebcb Mon Sep 17 00:00:00 2001 From: Richard Senior Date: Wed, 17 May 2017 20:55:17 +0100 Subject: [PATCH 3/8] Add new visitor to create PDF checklists --- build.gradle | 4 + src/main/java/org/flightgear/clgen/CLGen.java | 2 + .../flightgear/clgen/backend/PdfVisitor.java | 157 ++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 src/main/java/org/flightgear/clgen/backend/PdfVisitor.java diff --git a/build.gradle b/build.gradle index 634bc26..de4baa1 100644 --- a/build.gradle +++ b/build.gradle @@ -18,12 +18,16 @@ generateGrammarSource { repositories { mavenCentral() + maven { + url "https://jitpack.io" + } } dependencies { antlr 'org.antlr:antlr4:4.7' compile 'org.antlr:antlr4-runtime:4.7' compile 'org.apache.velocity:velocity:1.7' + compile 'com.github.ralfstuckert.pdfbox-layout:pdfbox2-layout:0.9.0' testCompile 'org.codehaus.groovy:groovy-all:2.4.11' testCompile 'cglib:cglib-nodep:3.2.5' testCompile 'org.spockframework:spock-core:1.0-groovy-2.4' diff --git a/src/main/java/org/flightgear/clgen/CLGen.java b/src/main/java/org/flightgear/clgen/CLGen.java index 77190be..48a90c1 100644 --- a/src/main/java/org/flightgear/clgen/CLGen.java +++ b/src/main/java/org/flightgear/clgen/CLGen.java @@ -29,6 +29,7 @@ import org.flightgear.clgen.ast.AbstractSyntaxTree; import org.flightgear.clgen.ast.Item; import org.flightgear.clgen.backend.DotVisitor; +import org.flightgear.clgen.backend.PdfVisitor; import org.flightgear.clgen.backend.UsageVisitor; import org.flightgear.clgen.backend.XmlVisitor; import org.flightgear.clgen.listener.ChecklistListener; @@ -129,6 +130,7 @@ private void run() throws IOException, GeneratorException { warnings += usageVisitor.getNumberOfWarnings(); ast.accept(new XmlVisitor(input.toAbsolutePath().getParent())); ast.accept(new DotVisitor(input.toAbsolutePath().getParent())); + ast.accept(new PdfVisitor(input.toAbsolutePath().getParent())); if (warnings > 0) System.out.format( "Generation complete with %d warning%s.\n", diff --git a/src/main/java/org/flightgear/clgen/backend/PdfVisitor.java b/src/main/java/org/flightgear/clgen/backend/PdfVisitor.java new file mode 100644 index 0000000..a873391 --- /dev/null +++ b/src/main/java/org/flightgear/clgen/backend/PdfVisitor.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2017 Richard Senior + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.flightgear.clgen.backend; + +import java.io.IOException; +import java.nio.file.Path; + +import org.apache.pdfbox.pdmodel.font.PDType1Font; +import org.flightgear.clgen.GeneratorException; +import org.flightgear.clgen.ast.AbstractSyntaxTree; +import org.flightgear.clgen.ast.Check; +import org.flightgear.clgen.ast.Checklist; + +import rst.pdfbox.layout.elements.Document; +import rst.pdfbox.layout.elements.Paragraph; +import rst.pdfbox.layout.elements.VerticalSpacer; +import rst.pdfbox.layout.elements.render.RenderContext; +import rst.pdfbox.layout.elements.render.RenderListener; +import rst.pdfbox.layout.elements.render.VerticalLayoutHint; +import rst.pdfbox.layout.text.Alignment; +import rst.pdfbox.layout.text.FontDescriptor; +import rst.pdfbox.layout.text.Position; +import rst.pdfbox.layout.text.TextFlow; +import rst.pdfbox.layout.text.TextFlowUtil; +import rst.pdfbox.layout.text.TextSequenceUtil; + +/** + * Creates a PDF representation of the checklists. + * + * @author Richard Senior + */ +public class PdfVisitor extends AbstractVisitor implements RenderListener { + + private static final FontDescriptor normal; + private static final FontDescriptor heading; + + private final Path outputDir; + private final Document document; + + static { + normal = new FontDescriptor(PDType1Font.COURIER, 12.0f); + heading = new FontDescriptor(PDType1Font.COURIER_BOLD, 12.0f); + } + + public PdfVisitor(final Path outputDir) { + this.outputDir = outputDir; + document = new Document(70, 70, 50, 100); + document.addRenderListener(this); + } + + @Override + public void enter(final AbstractSyntaxTree ast) { + try { + Paragraph p = new Paragraph(); + p.addText("CHECKLIST", 18, PDType1Font.COURIER_BOLD); + document.add(p, VerticalLayoutHint.CENTER); + document.add(new VerticalSpacer(18)); + } catch (IOException e) { + throw new GeneratorException(e); + } + } + + @Override + public void exit(final AbstractSyntaxTree ast) { + try { + Path path = outputDir.resolve("checklists.pdf"); + document.save(path.toFile()); + System.out.println(path.toAbsolutePath().normalize().toString()); + } catch (IOException e) { + throw new GeneratorException(e); + } + } + + @Override + public void enter(final Checklist checklist) { + try { + Paragraph p = new Paragraph(); + p.addText(checklist.getTitle().toUpperCase(), heading.getSize(), heading.getFont()); + document.add(p); + } catch (IOException e) { + throw new GeneratorException(e); + } + } + + @Override + public void exit(final Checklist checklist) { + document.add(new VerticalSpacer(24)); + } + + @Override + public void enter(final Check check) { + try { + Paragraph p = new Paragraph(); + p.setLineSpacing(1.5f); + String s = String.format("%s %s %s", + check.getItem().getName(), + dots(check.getItem().getName(), check.getState().getName(), textWidth(normal) - 2), + check.getState().getName() + ); + p.addText(s, normal.getSize(), normal.getFont()); + for (String value : check.getAdditionalValues()) { + p.setAlignment(Alignment.Right); + p.addText(value + "\n", normal.getSize(), normal.getFont()); + } + document.add(p); + } catch (IOException e) { + throw new GeneratorException(e); + } + } + + private int textWidth(final FontDescriptor fontDescriptor) { + try { + float w = TextSequenceUtil.getEmWidth(fontDescriptor); + return (int)Math.floor(document.getPageWidth() / w); + } catch (IOException e) { + throw new GeneratorException(e); + } + } + + private String dots(final String pre, final String post, int width) { + width -= pre.length(); + width -= post.length(); + StringBuilder sb = new StringBuilder(); + while (width-- > 0) + sb.append('.'); + return sb.toString(); + } + + // Render Listener + + @Override + public void beforePage(final RenderContext renderContext) throws IOException {} + + @Override + public void afterPage(final RenderContext renderContext) throws IOException { + String content = String.format("%s", renderContext.getPageIndex() + 1); + TextFlow text = TextFlowUtil.createTextFlow(content, normal.getSize(), normal.getFont()); + float offset = renderContext.getPageFormat().getMarginLeft(); + offset += TextSequenceUtil.getOffset(text, renderContext.getWidth(), Alignment.Center); + Position p = new Position(offset, 60); + text.drawText(renderContext.getContentStream(), p, Alignment.Center, null); + } +} From f7aec9eac7ee635f6b77f79f5b5fa2a944a453db Mon Sep 17 00:00:00 2001 From: Richard Senior Date: Wed, 17 May 2017 23:24:01 +0100 Subject: [PATCH 4/8] Optimize nested logical conditions (Issue #1) --- .../flightgear/clgen/backend/XmlVisitor.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/flightgear/clgen/backend/XmlVisitor.java b/src/main/java/org/flightgear/clgen/backend/XmlVisitor.java index de1c2df..e0cbbfb 100644 --- a/src/main/java/org/flightgear/clgen/backend/XmlVisitor.java +++ b/src/main/java/org/flightgear/clgen/backend/XmlVisitor.java @@ -78,6 +78,8 @@ public class XmlVisitor extends AbstractVisitor { private final Deque wrapperElements = new ArrayDeque<>(); private final Deque elements = new ArrayDeque<>(); + private final Deque binaryConditions = new ArrayDeque<>(); + public XmlVisitor(final Path outputDir) throws GeneratorException { this.outputDir = outputDir; DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); @@ -205,14 +207,19 @@ public void exit(final Condition condition) { @Override public void enter(final BinaryCondition condition) { - Element e = document.createElement(operatorTag(condition.getOperator())); - elements.peek().appendChild(e); - elements.push(e); + if (nestedCondition(condition)) { + Element e = document.createElement(operatorTag(condition.getOperator())); + elements.peek().appendChild(e); + elements.push(e); + } + binaryConditions.push(condition); } @Override public void exit(final BinaryCondition condition) { - elements.pop(); + binaryConditions.pop(); + if (nestedCondition(condition)) + elements.pop(); } @Override @@ -335,6 +342,11 @@ private String filename(final Checklist checklist) { return s + ".xml"; } + private boolean nestedCondition(final BinaryCondition condition) { + return binaryConditions.isEmpty() || + condition.getOperator() != binaryConditions.peek().getOperator(); + } + private String operatorTag(final Operator op) { switch (op) { case AND: return "and"; From 29632396354754ab5f4aa2df6c36e7fb0b3c4cff Mon Sep 17 00:00:00 2001 From: Richard Senior Date: Thu, 18 May 2017 11:18:09 +0100 Subject: [PATCH 5/8] Reimplement PDF generation with OpenPDF --- build.gradle | 5 +- .../flightgear/clgen/backend/PdfVisitor.java | 151 +++++++++--------- 2 files changed, 76 insertions(+), 80 deletions(-) diff --git a/build.gradle b/build.gradle index de4baa1..03b01c5 100644 --- a/build.gradle +++ b/build.gradle @@ -18,16 +18,13 @@ generateGrammarSource { repositories { mavenCentral() - maven { - url "https://jitpack.io" - } } dependencies { antlr 'org.antlr:antlr4:4.7' compile 'org.antlr:antlr4-runtime:4.7' compile 'org.apache.velocity:velocity:1.7' - compile 'com.github.ralfstuckert.pdfbox-layout:pdfbox2-layout:0.9.0' + compile 'com.github.librepdf:openpdf:1.0.1' testCompile 'org.codehaus.groovy:groovy-all:2.4.11' testCompile 'cglib:cglib-nodep:3.2.5' testCompile 'org.spockframework:spock-core:1.0-groovy-2.4' diff --git a/src/main/java/org/flightgear/clgen/backend/PdfVisitor.java b/src/main/java/org/flightgear/clgen/backend/PdfVisitor.java index a873391..b13dd34 100644 --- a/src/main/java/org/flightgear/clgen/backend/PdfVisitor.java +++ b/src/main/java/org/flightgear/clgen/backend/PdfVisitor.java @@ -16,119 +16,114 @@ */ package org.flightgear.clgen.backend; -import java.io.IOException; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.nio.file.Path; -import org.apache.pdfbox.pdmodel.font.PDType1Font; import org.flightgear.clgen.GeneratorException; import org.flightgear.clgen.ast.AbstractSyntaxTree; import org.flightgear.clgen.ast.Check; import org.flightgear.clgen.ast.Checklist; -import rst.pdfbox.layout.elements.Document; -import rst.pdfbox.layout.elements.Paragraph; -import rst.pdfbox.layout.elements.VerticalSpacer; -import rst.pdfbox.layout.elements.render.RenderContext; -import rst.pdfbox.layout.elements.render.RenderListener; -import rst.pdfbox.layout.elements.render.VerticalLayoutHint; -import rst.pdfbox.layout.text.Alignment; -import rst.pdfbox.layout.text.FontDescriptor; -import rst.pdfbox.layout.text.Position; -import rst.pdfbox.layout.text.TextFlow; -import rst.pdfbox.layout.text.TextFlowUtil; -import rst.pdfbox.layout.text.TextSequenceUtil; +import com.lowagie.text.Document; +import com.lowagie.text.DocumentException; +import com.lowagie.text.Element; +import com.lowagie.text.Font; +import com.lowagie.text.Paragraph; +import com.lowagie.text.Phrase; +import com.lowagie.text.pdf.BaseFont; +import com.lowagie.text.pdf.ColumnText; +import com.lowagie.text.pdf.PdfPageEventHelper; +import com.lowagie.text.pdf.PdfWriter; /** * Creates a PDF representation of the checklists. * * @author Richard Senior */ -public class PdfVisitor extends AbstractVisitor implements RenderListener { +public class PdfVisitor extends AbstractVisitor { - private static final FontDescriptor normal; - private static final FontDescriptor heading; + private static final Font H1 = new Font(Font.COURIER, 14.0f, Font.BOLD); + private static final Font H2 = new Font(Font.COURIER, 12.0f, Font.BOLD); + private static final Font P = new Font(Font.COURIER, 12.0f, Font.NORMAL); - private final Path outputDir; - private final Document document; + private static final float MARGIN = 70.0f; - static { - normal = new FontDescriptor(PDType1Font.COURIER, 12.0f); - heading = new FontDescriptor(PDType1Font.COURIER_BOLD, 12.0f); - } + private final Path filename; + private final Document document = new Document(); public PdfVisitor(final Path outputDir) { - this.outputDir = outputDir; - document = new Document(70, 70, 50, 100); - document.addRenderListener(this); + try { + document.setMargins(MARGIN, MARGIN, MARGIN, MARGIN); + filename = outputDir.resolve("checklists.pdf"); + FileOutputStream out = new FileOutputStream(filename.toFile()); + PdfWriter writer = PdfWriter.getInstance(document, out); + writer.setPageEvent(new Footer()); + document.open(); + } catch (FileNotFoundException | DocumentException e) { + document.close(); + throw new GeneratorException(e); + } } @Override public void enter(final AbstractSyntaxTree ast) { try { - Paragraph p = new Paragraph(); - p.addText("CHECKLIST", 18, PDType1Font.COURIER_BOLD); - document.add(p, VerticalLayoutHint.CENTER); - document.add(new VerticalSpacer(18)); - } catch (IOException e) { + Paragraph t = new Paragraph("CHECKLIST", H1); + t.setSpacingAfter(12.0f); + t.setAlignment(Element.ALIGN_CENTER); + document.add(t); + } catch (DocumentException e) { + document.close(); throw new GeneratorException(e); } } @Override public void exit(final AbstractSyntaxTree ast) { - try { - Path path = outputDir.resolve("checklists.pdf"); - document.save(path.toFile()); - System.out.println(path.toAbsolutePath().normalize().toString()); - } catch (IOException e) { - throw new GeneratorException(e); - } + document.close(); + System.out.println(filename.toAbsolutePath().normalize().toString()); } @Override public void enter(final Checklist checklist) { try { - Paragraph p = new Paragraph(); - p.addText(checklist.getTitle().toUpperCase(), heading.getSize(), heading.getFont()); + Paragraph p = new Paragraph(checklist.getTitle().toUpperCase(), H2); + p.setSpacingBefore(24.0f); document.add(p); - } catch (IOException e) { + } catch (DocumentException e) { + document.close(); throw new GeneratorException(e); } } - @Override - public void exit(final Checklist checklist) { - document.add(new VerticalSpacer(24)); - } - @Override public void enter(final Check check) { try { - Paragraph p = new Paragraph(); - p.setLineSpacing(1.5f); - String s = String.format("%s %s %s", - check.getItem().getName(), - dots(check.getItem().getName(), check.getState().getName(), textWidth(normal) - 2), - check.getState().getName() - ); - p.addText(s, normal.getSize(), normal.getFont()); + String i = check.getItem().getName(); + String s = check.getState().getName(); + String line = String.format("%s %s %s", i, dots(i, s, textWidth(P) - 2), s); + Paragraph p = new Paragraph(line, P); + p.setSpacingBefore(6.0f); + document.add(p); for (String value : check.getAdditionalValues()) { - p.setAlignment(Alignment.Right); - p.addText(value + "\n", normal.getSize(), normal.getFont()); + p = new Paragraph(value, P); + p.setSpacingBefore(6.0f); + p.setAlignment(Element.ALIGN_RIGHT); + document.add(p); } - document.add(p); - } catch (IOException e) { + } catch (DocumentException e) { + document.close(); throw new GeneratorException(e); } } - private int textWidth(final FontDescriptor fontDescriptor) { - try { - float w = TextSequenceUtil.getEmWidth(fontDescriptor); - return (int)Math.floor(document.getPageWidth() / w); - } catch (IOException e) { - throw new GeneratorException(e); - } + // Other methods + + private int textWidth(final Font font) { + BaseFont b = font.getCalculatedBaseFont(false); + float w = b.getWidthPoint(".", font.getSize()); + return (int)Math.floor((document.getPageSize().getWidth() - MARGIN * 2) / w); } private String dots(final String pre, final String post, int width) { @@ -140,18 +135,22 @@ private String dots(final String pre, final String post, int width) { return sb.toString(); } - // Render Listener + // Page Event Helper - @Override - public void beforePage(final RenderContext renderContext) throws IOException {} + private static final class Footer extends PdfPageEventHelper { + + @Override + public void onEndPage(final PdfWriter writer, final Document document) { + Phrase pageNo = new Phrase(Integer.toString(document.getPageNumber()), P); + float x = document.getPageSize().getWidth() / 2; + float y = MARGIN / 2; + ColumnText.showTextAligned( + writer.getDirectContent(), + Element.ALIGN_CENTER, + pageNo, x, y, 0f + ); + } - @Override - public void afterPage(final RenderContext renderContext) throws IOException { - String content = String.format("%s", renderContext.getPageIndex() + 1); - TextFlow text = TextFlowUtil.createTextFlow(content, normal.getSize(), normal.getFont()); - float offset = renderContext.getPageFormat().getMarginLeft(); - offset += TextSequenceUtil.getOffset(text, renderContext.getWidth(), Alignment.Center); - Position p = new Position(offset, 60); - text.drawText(renderContext.getContentStream(), p, Alignment.Center, null); } + } From 45ca73d82c752872b0d1b332db02afce9a28ef15 Mon Sep 17 00:00:00 2001 From: Richard Senior Date: Thu, 18 May 2017 11:56:51 +0100 Subject: [PATCH 6/8] Exclude signing certificates from signed jars in fat jar --- build.gradle | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 03b01c5..63806ab 100644 --- a/build.gradle +++ b/build.gradle @@ -35,8 +35,11 @@ jar { attributes('Main-Class': 'org.flightgear.clgen.CLGen') } from { - configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } + configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) } } + exclude "META-INF/*.SF" + exclude "META-INF/*.DSA" + exclude "META-INF/*.RSA" } task release(type: Zip, dependsOn: build) { From 9b70a1801c3a2b17a5a56ea754cf4d21c2b8c63e Mon Sep 17 00:00:00 2001 From: Richard Senior Date: Thu, 18 May 2017 12:17:03 +0100 Subject: [PATCH 7/8] Some changes to sample checklists --- samples/Beagle-Pup/pup.clg | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/samples/Beagle-Pup/pup.clg b/samples/Beagle-Pup/pup.clg index b8998ec..e5aba6c 100644 --- a/samples/Beagle-Pup/pup.clg +++ b/samples/Beagle-Pup/pup.clg @@ -49,14 +49,15 @@ item("Beacon") { volts = "systems/electrical/outputs/beacon"; breaker = "controls/switches/circuit-breakers/beacon"; beaconChecked = "sim/checklists/status/beacon"; - state("OFF", volts == 0 && beaconChecked) { + state("OFF", volts == 0) { fgcommand("switch-assign", property=breaker, value=false); } state("ON", volts > 0) { fgcommand("switch-assign", property=breaker, value=true); } - state("CHECK", volts > 0 || beaconChecked) { + state("CHECK & OFF", volts == 0 && beaconChecked) { fgcommand("switch-assign", property=breaker, value=true); + fgcommand("switch-assign", property=breaker, value=false); } } @@ -141,6 +142,7 @@ item("Flaps") { item("Fuel Tank Selector") { tank1 = "consumables/fuel/tank[1]/selected"; tank2 = "consumables/fuel/tank[2]/selected"; + state("EITHER", tank1 || tank2) fgcommand("tank-select-left"); state("BOTH", tank1 && tank2) fgcommand("tank-select-both"); state("OFF", !(tank1 || tank2)) fgcommand("tank-select-off"); } @@ -227,7 +229,7 @@ item("Pitot Heater") { item("Carburettor") { min = "fdm/jsbsim/propulsion/engine/prime-min"; primeLevel= "fdm/jsbsim/propulsion/engine/prime-norm"; - state("PRIMED", primeLevel >= min) primeLevel = 1; + state("PRIME", primeLevel >= min) primeLevel = 1; } item("Propeller") { @@ -309,8 +311,7 @@ checklist("Before Starting Engines") { check("Parking Brake", "ON"); check("Main Battery", "CHECK"); check("Navigation Lights", "CHECK"); - check("Beacon", "CHECK"); - check("Beacon", "OFF"); + check("Beacon", "CHECK & OFF"); check("Main Battery", "OFF"); check("Refuel", "AS REQUIRED"); } @@ -318,10 +319,10 @@ checklist("Before Starting Engines") { checklist("Start Engines") { check("Parking Brake", "ON"); check("Carb Heat", "COLD"); - check("Fuel Tank Selector", "BOTH"); + check("Fuel Tank Selector", "EITHER"); check("Mixture", "FULL RICH"); check("Circuit Breakers", "ALL IN"); - check("Carburettor", "PRIMED", "(1-4 strokes of throttle)"); + check("Carburettor", "PRIME", "(1-4 strokes of throttle)"); check("Throttle", "APPROX 1/4"); check("Main Battery", "ON"); check("Navigation Lights", "ON"); @@ -334,16 +335,20 @@ checklist("Start Engines") { check("Engine", "START"); } -checklist("Before Taxi") { - check("Throttle", "TICK OVER"); +checklist("During Warm Up") { + check("Throttle", "TICK OVER", "(800-1200rpm)"); check("Oil Pressure", "CHECK", "(minimum 25psi)"); + check("Suction", "CHECK", "(minimum 3\")"); check("Starter Master", "OFF"); check("Boost Pump", "OFF"); check("Alternator Warning", "OFF"); - check("Transponder", "SBY"); + check("Carb Heat", "AS REQUIRED"); +} + +checklist("Before Taxi") { check("Autopilot", "CHECK"); + check("Transponder", "SBY"); check("Radios", "ON"); - check("Suction", "CHECK", "(minimum 3\")"); check("Heading Indicator", "ALIGN"); check("Altimeter", "SET"); check("Taxi Light", "ON"); @@ -386,11 +391,11 @@ checklist("Traffic Pattern") { } checklist("After Landing") { - check("Taxi Light", "ON"); - check("Flaps", "UP"); - check("Boost Pump", "OFF"); - check("Carb Heat", "COLD"); check("Pitot Heater", "OFF"); + check("Carb Heat", "COLD"); + check("Boost Pump", "OFF"); + check("Flaps", "UP"); + check("Taxi Light", "ON"); check("Transponder", "SBY"); } @@ -399,7 +404,7 @@ checklist("Parking") { check("Taxi Light", "OFF"); check("Transponder", "OFF"); check("Radios", "OFF"); - check("Throttle", "TICK OVER"); + check("Throttle", "TICK OVER", "(800-1200rpm)"); check("Mixture", "IDLE CUTOFF"); check("Beacon", "OFF"); check("Alternator", "OFF"); From a1e3ab805a1a16258591eff66560705c64fe7a80 Mon Sep 17 00:00:00 2001 From: Richard Senior Date: Thu, 18 May 2017 15:07:23 +0100 Subject: [PATCH 8/8] Update version to 1.1.1 --- build.gradle | 2 +- clgen | 2 +- clgen.bat | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 63806ab..516f3ca 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'antlr' apply plugin: 'groovy' apply plugin: 'java' -project.version = '1.1.0' +project.version = '1.1.1' defaultTasks 'build' diff --git a/clgen b/clgen index a739dd2..4ff0e38 100755 --- a/clgen +++ b/clgen @@ -1,2 +1,2 @@ #!/bin/sh -java -jar $(dirname $0)/build/libs/CLGen-1.1.0.jar $* +java -jar $(dirname $0)/build/libs/CLGen-1.1.1.jar $* diff --git a/clgen.bat b/clgen.bat index 2ac4326..3c99591 100644 --- a/clgen.bat +++ b/clgen.bat @@ -1,2 +1,2 @@ @echo off -java -jar %~dp0/build/libs/CLGen-1.1.0.jar %* +java -jar %~dp0/build/libs/CLGen-1.1.1.jar %*