diff --git a/GUIDE.md b/GUIDE.md index b01dc18..a66f207 100644 --- a/GUIDE.md +++ b/GUIDE.md @@ -370,8 +370,8 @@ d.layout("C-101", { paper: "ARCH D" }, (sheet) => { - Inside `layout`, every length is on the paper, in the paper's units: inches for every paper tsdraft has. A text 1/8 inch high has `height: 0.125`. `sheet.width` and `sheet.height` are the paper's size, and its lower-left corner is `[0, 0]`. -- The papers are `ARCH A` to `ARCH D` and `ANSI A` to `ANSI D`, each landscape and full - bleed. `ARCH D` is 36 by 24. +- The papers are `ARCH A` to `ARCH E` and `ANSI A` to `ANSI D`, each landscape and full + bleed. `ARCH D` is 36 by 24, and `ARCH E` 48 by 36. - `sheet.viewport(corner, width, height, { center, ratio })` shows model space in a rectangle on the paper. `center` is the model point at its middle. `ratio` is the one you give `fromPaper`: 120 is 1" = 10'. @@ -381,7 +381,7 @@ d.layout("C-101", { paper: "ARCH D" }, (sheet) => { pattern times it, in every viewport. The picture of model space draws the same dashes that many drawing units long, often too short to see, so judge linetypes on the sheet's picture. - The sheet plots in black with `monochrome.ctb`. Pass `plotStyle: "acad.ctb"` to plot each - layer in its color. + layer in its color, or `"Grayscale.ctb"` in grays. `PLOT_STYLE_TABLES` lists them. - Define blocks outside `layout`, and insert them inside it. An `id` is unique across model space, blocks and every layout. - A title block is a block with an attribute for each field, and a firm's own border is an diff --git a/docs/layouts.md b/docs/layouts.md index 09ea18b..a139598 100644 --- a/docs/layouts.md +++ b/docs/layouts.md @@ -45,7 +45,8 @@ puts entities on a layer as it does anywhere. ### Paper `paper` is one of `PAPERS`. Each is a size that AutoCAD's `DWG To PDF.pc3` offers as full -bleed, with no margin the plotter keeps clear, and landscape. +bleed, with no margin the plotter keeps clear, and a layout of each is landscape, with the +longer side across. | Paper | Inches | | -------- | --------- | @@ -53,11 +54,18 @@ bleed, with no margin the plotter keeps clear, and landscape. | `ARCH B` | 18 by 12 | | `ARCH C` | 24 by 18 | | `ARCH D` | 36 by 24 | +| `ARCH E` | 48 by 36 | | `ANSI A` | 11 by 8.5 | | `ANSI B` | 17 by 11 | | `ANSI C` | 22 by 17 | | `ANSI D` | 34 by 22 | +The plotter holds `ARCH E` upright, as `ARCH_full_bleed_E_(36.00_x_48.00_Inches)`, 36 wide +and 48 tall. Its page setup stores that size and turns the plot a quarter turn onto it, as +AutoCAD's own landscape page setup on that paper does, and the PDF shows the page landscape, +48 by 36, with the drawing upright. `PaperInfo.medium` holds the size as the plotter holds it +and the turn. + The paper's units are the units of the sheet: on these, an inch. The sheet's lower-left corner is `[0, 0]`. Sizes drawn on a sheet are the sizes they plot at, so a text 1/8 inch high has a height of `0.125`, whatever the drawing's own units are. @@ -76,13 +84,25 @@ Every layout has the same page setup, apart from its paper and its plot style ta | Plotter | `DWG To PDF.pc3` | | What to plot | The layout | | Scale | 1:1 | -| Plot style table | `monochrome.ctb`, or `acad.ctb` | +| Plot style table | One of `PLOT_STYLE_TABLES` | | Lineweights | Plotted, each line as wide as its lineweight | -| Orientation | Landscape, as the paper is | +| Orientation | Landscape | + +`plotStyle` is one of `PLOT_STYLE_TABLES`, the color-dependent tables AutoCAD installs. Each +plots a line as wide as its lineweight, apart from `DWF Virtual Pens.ctb`: + +| Table | Colors | +| ---------------------- | ---------------------------------------------------------------- | +| `monochrome.ctb` | Every color in black. The default | +| `acad.ctb` | Each color in its own, and 7 in black | +| `Grayscale.ctb` | Each color in a gray of its own, and 7 in black | +| `DWF Virtual Pens.ctb` | Each color in the color of its pen, and every line 0.254 mm wide | -`plotStyle` is `"monochrome.ctb"` by default, which plots every color in black. With -`"acad.ctb"` each layer plots in its own color. AutoCAD plots a layout whose plot style table -it does not have with no table, each pen in its own color. +A pen of `DWF Virtual Pens.ctb` has a color of its own, which is near its index's but not +always the same: 9 plots as `#c0c0c0`, and 12 as `#a50000`. The table also gives every pen one +lineweight, so a line plots 0.254 mm wide whatever its own lineweight, and so does a viewport's +border. `toSVG` draws a sheet in the colors and widths its table plots. AutoCAD plots a layout +whose plot style table it does not have with no table, each pen in its own color. ### Names @@ -186,10 +206,11 @@ set of ids. no clip. - A viewport `lint` reports an error for is drawn as its border alone. - Every line is as wide as its lineweight on the paper, inside a viewport too, and dashes are - as long as they plot. + as long as they plot. Under `DWF Virtual Pens.ctb` every line is 0.254 mm wide. - Colors are the ones the plot style table plots: black for `monochrome.ctb`. -- A viewport's border is drawn, as the thinnest line, when its layer is on, thawed and set to - plot. With `drawNoPlot` it is drawn on a layer that does not plot too. +- A viewport's border is drawn, as the thinnest line or 0.254 mm wide under + `DWF Virtual Pens.ctb`, when its layer is on, thawed and set to plot. With `drawNoPlot` it + is drawn on a layer that does not plot too. Without `layout`, `toSVG` draws model space as it always has. diff --git a/docs/rules.md b/docs/rules.md index 8cb3124..62e105d 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -147,9 +147,9 @@ it would take `1e+100` for `1e+10` and report nothing. tab. See [Names](layouts.md#names). - No two layouts have names AutoCAD takes for one, as `Sheet` and `SHEET` are. AutoCAD keeps one of the two, and its `AUDIT` reports the other as missing from the dictionary of layouts. -- A layout's paper is one of `PAPERS`, and its plot style table `monochrome.ctb` or - `acad.ctb`. AutoCAD plots nothing for a layout whose paper the plotter knows neither by - name nor by size, and plots one whose plot style table it does not have with no table. +- A layout's paper is one of `PAPERS`, and its plot style table one of `PLOT_STYLE_TABLES`. + AutoCAD plots nothing for a layout whose paper the plotter knows neither by name nor by + size, and plots one whose plot style table it does not have with no table. - A viewport's corner and center are points, its width and height are sizes, and its ratio is greater than 0. The height of model space it shows, which a file holds in place of a scale, is not written as 0 and is under 1e100. diff --git a/src/dxf-r2018.ts b/src/dxf-r2018.ts index 9077921..fb5c3e6 100644 --- a/src/dxf-r2018.ts +++ b/src/dxf-r2018.ts @@ -43,6 +43,7 @@ import { import { DEFAULT_PLOT_STYLE_TABLE, fromMillimetres, + mediumMargins, paperInfo, paperMillimetres, PLOTTER, @@ -1348,9 +1349,8 @@ function writeLayout( box: readonly [number, number, number, number], ): void { const { layout, paper, handles } = sheet; - const [left, bottom, right, top] = paper.printable; const mm = (length: number): number => paperMillimetres(length, paper.units); - const [width, height] = [mm(paper.width), mm(paper.height)]; + const [left, bottom, right, top] = mediumMargins(paper); g(0, "LAYOUT"); ownedBy(g, handles.layout, dictionary); g(100, "AcDbPlotSettings"); @@ -1358,12 +1358,13 @@ function writeLayout( g(2, PLOTTER); g(4, paper.media); g(6, ""); // plot view - g(40, mm(left)); // the margins the plotter keeps clear + // The margins the plotter keeps clear and the medium's size, as the plotter holds it. + g(40, mm(left)); g(41, mm(bottom)); - g(42, mm(paper.width - right)); - g(43, mm(paper.height - top)); - g(44, width); - g(45, height); + g(42, mm(right)); + g(43, mm(top)); + g(44, mm(paper.medium.width)); + g(45, mm(paper.medium.height)); g(46, 0); // plot origin g(47, 0); g(48, 0); // plot window, lower left @@ -1374,7 +1375,7 @@ function writeLayout( g(143, 1); // custom scale: drawing units g(70, PLOT_VIEWPORTS_FIRST | PLOT_LINEWEIGHTS | PLOT_PLOT_STYLES | PLOT_USE_STANDARD_SCALE); g(72, paper.units === "in" ? 0 : 1); - g(73, 0); // rotation: none, the paper is landscape as it is + g(73, paper.medium.rotation); // the plot's rotation onto the medium g(74, 5); // plot area: the layout g(7, layout.plotStyle ?? DEFAULT_PLOT_STYLE_TABLE); g(75, 16); // standard scale: 1:1 diff --git a/src/index.ts b/src/index.ts index cf8d516..c31d7ef 100644 --- a/src/index.ts +++ b/src/index.ts @@ -99,7 +99,13 @@ export { DrawingError, lint, type Diagnostic, type LintOptions, type LintRule } export { CENTER, DASHDOT, DASHED, DOT, HIDDEN, LINEWEIGHTS, PHANTOM } from "./linetypes.ts"; export { measureText, type TextMeasure, type TextToMeasure } from "./measure.ts"; export { fromPaper, type PaperScale, type PaperUnit } from "./paper.ts"; -export { PAPERS, type Paper, type PaperInfo, type PlotStyleTable } from "./papers.ts"; +export { + PAPERS, + PLOT_STYLE_TABLES, + type Paper, + type PaperInfo, + type PlotStyleTable, +} from "./papers.ts"; export type { Aci, ArrowheadOf, diff --git a/src/papers.ts b/src/papers.ts index 1df9de5..b78279a 100644 --- a/src/papers.ts +++ b/src/papers.ts @@ -8,9 +8,20 @@ export interface PaperInfo { readonly media: string; /** The unit of the size, and of everything drawn on a layout of this paper. */ readonly units: PaperUnit; - /** The width of the sheet, which is its longer side. */ + /** The width of a layout of this paper, which is its longer side. */ readonly width: number; readonly height: number; + /** + * The medium as the plotter holds it, which the page setup stores: its width and height, and + * the plot's rotation on it, as DXF group 73 holds it. 0 plots the layout as it is, on a medium + * as wide as the layout. 1 turns it a quarter turn onto a medium held upright, as AutoCAD's own + * landscape page setup on that medium does, and the PDF shows the page turned back, landscape. + */ + readonly medium: { + readonly width: number; + readonly height: number; + readonly rotation: 0 | 1; + }; /** * The part of the sheet the plotter prints on, as its lower-left and upper-right corners * from the sheet's lower-left corner: `[left, bottom, right, top]`. A full-bleed size prints @@ -21,27 +32,33 @@ export interface PaperInfo { /** * Every paper a layout may have. Each is a size that AutoCAD's `DWG To PDF.pc3` offers as full - * bleed, with no margin the plotter keeps clear, and landscape, with the longer side across. - * The list is closed because a page setup names its paper by the name the plotter gives it, - * and AutoCAD plots a layout whose paper the plotter does not have on another size. + * bleed, with no margin the plotter keeps clear, and a layout of each is landscape, with the + * longer side across. The plotter holds `ARCH E` upright, so its plot is turned a quarter turn + * onto it. The list is closed because a page setup names its paper by the name the plotter + * gives it, and AutoCAD plots a layout whose paper the plotter does not have on another size. */ export const PAPERS = { "ARCH A": inches("ARCH_full_bleed_A_(12.00_x_9.00_Inches)", 12, 9), "ARCH B": inches("ARCH_full_bleed_B_(18.00_x_12.00_Inches)", 18, 12), "ARCH C": inches("ARCH_full_bleed_C_(24.00_x_18.00_Inches)", 24, 18), "ARCH D": inches("ARCH_full_bleed_D_(36.00_x_24.00_Inches)", 36, 24), + "ARCH E": inches("ARCH_full_bleed_E_(36.00_x_48.00_Inches)", 48, 36, 1), "ANSI A": inches("ANSI_full_bleed_A_(11.00_x_8.50_Inches)", 11, 8.5), "ANSI B": inches("ANSI_full_bleed_B_(17.00_x_11.00_Inches)", 17, 11), "ANSI C": inches("ANSI_full_bleed_C_(22.00_x_17.00_Inches)", 22, 17), "ANSI D": inches("ANSI_full_bleed_D_(34.00_x_22.00_Inches)", 34, 22), } as const satisfies Readonly>; +/** A paper in inches, `width` across the layout, on a medium the plot turns `rotation` onto. */ function inches( media: M, width: W, height: H, + rotation: 0 | 1 = 0, ) { - return { media, units: "in", width, height, printable: [0, 0, width, height] } as const; + const medium = + rotation === 0 ? { width, height, rotation } : { width: height, height: width, rotation }; + return { media, units: "in", width, height, medium, printable: [0, 0, width, height] } as const; } /** The name of a paper a layout may have. */ @@ -54,10 +71,17 @@ export function paperInfo(name: string): PaperInfo | undefined { /** * The plot style tables a layout's page setup may name, which are color-dependent tables that - * AutoCAD installs. `monochrome.ctb` plots every color in black, and `acad.ctb` plots each in - * its own color. Both plot a line at the lineweight it has. + * AutoCAD installs. `monochrome.ctb` plots every color in black, `acad.ctb` each in its own + * color, `Grayscale.ctb` each in a gray of its own, and `DWF Virtual Pens.ctb` each in the color + * of its pen. Each but `DWF Virtual Pens.ctb` plots a line at the lineweight it has; that one + * plots every line 0.254 mm wide. */ -export const PLOT_STYLE_TABLES = ["monochrome.ctb", "acad.ctb"] as const; +export const PLOT_STYLE_TABLES = [ + "monochrome.ctb", + "acad.ctb", + "Grayscale.ctb", + "DWF Virtual Pens.ctb", +] as const; /** The name of a plot style table a layout may have. */ export type PlotStyleTable = (typeof PLOT_STYLE_TABLES)[number]; @@ -78,6 +102,21 @@ export function paperMillimetres(length: number, units: PaperUnit): number { return Math.fround(units === "in" ? length * MM_PER_INCH : length); } +/** + * The strip the plotter keeps clear at each edge of the medium, `[left, bottom, right, top]` in + * the paper's units, as the page setup holds them: in the medium's own orientation. A plot + * turned onto a medium held upright puts the layout's bottom edge on the medium's left and the + * layout's left edge at the medium's top. + */ +export function mediumMargins( + paper: PaperInfo, +): readonly [left: number, bottom: number, right: number, top: number] { + const [left, bottom, right, top] = paper.printable; + const margins = [left, bottom, paper.width - right, paper.height - top] as const; + if (paper.medium.rotation === 0) return margins; + return [margins[1], margins[2], margins[3], margins[0]]; +} + /** A length a page setup holds in millimetres, in the paper's units. */ export function fromMillimetres(length: number, units: PaperUnit): number { return units === "in" ? length / MM_PER_INCH : length; diff --git a/src/rules.ts b/src/rules.ts index bf5cf4b..dd8c2a2 100644 --- a/src/rules.ts +++ b/src/rules.ts @@ -150,9 +150,9 @@ const LAYOUT_NAME_LENGTH = const LAYOUT_NAME_MODEL = "A layout cannot be named Model, in any case of its letters. AutoCAD keeps that name for the model tab: it discards a drawing whose layout is named MODEL, takes a layout named model for the model tab's own and loses its sheet, and never finishes opening a drawing with a second layout named Model"; const PAPER = - 'A paper must be one of PAPERS, the full-bleed landscape sizes of AutoCAD\'s DWG To PDF.pc3, such as "ARCH D". A page setup names its paper as the plotter does, and AutoCAD plots nothing for a layout whose paper the plotter knows neither by its name nor by its size'; + 'A paper must be one of PAPERS, the full-bleed sizes of AutoCAD\'s DWG To PDF.pc3, such as "ARCH D", each laid out landscape. A page setup names its paper as the plotter does, and AutoCAD plots nothing for a layout whose paper the plotter knows neither by its name nor by its size'; const PLOT_STYLE = - 'A plot style table must be "monochrome.ctb" or "acad.ctb", which AutoCAD installs. AutoCAD plots a layout whose plot style table it does not have with no table, each pen in its own color'; + 'A plot style table must be "monochrome.ctb", "acad.ctb", "Grayscale.ctb" or "DWF Virtual Pens.ctb", which AutoCAD installs. AutoCAD plots a layout whose plot style table it does not have with no table, each pen in its own color'; const XREF_PATH_EMPTY = "An xref's path cannot be empty. AutoCAD then looks for a DWG file named as the xref is"; const XREF_PATH_DXF = diff --git a/src/svg.ts b/src/svg.ts index bb636da..ed0bbf7 100644 --- a/src/svg.ts +++ b/src/svg.ts @@ -74,6 +74,74 @@ function aciColor(index: number): string { return rgb === undefined ? "currentColor" : `#${rgb}`; } +/** The gray `Grayscale.ctb` plots each ACI index in, from 1 to 255, twenty to a row. */ +const GRAYSCALE: readonly string[] = [ + "4c e1 95 b2 1d 69 00 7f db 4c a4 42 8e 37 77 2c 5f 22 49 70", + "b7 62 9f 52 84 41 6a 32 51 96 ca 82 ae 6d 92 57 75 43 5a bc", + "dc a2 bf 88 9f 6d 80 54 62 e1 ef c3 cf a3 ad 83 8b 64 6a ce", + "e5 b2 c6 95 a6 78 86 5b 65 ba dc a1 be 87 9f 6d 80 53 61 a7", + "d2 91 b6 79 98 62 7a 4a 5d 95 c8 81 ad 6c 91 57 75 42 59 9c", + "cc 87 b0 71 94 5b 77 45 5b a3 cf 8d b3 76 96 5f 79 48 5c aa", + "d3 93 b7 7b 99 63 7b 4b 5e b2 d7 9a ba 80 9b 67 7d 4e 5f 8d", + "c4 79 aa 65 8d 51 72 3e 57 67 b2 59 99 4a 80 3b 67 2d 4f 41", + "9f 39 8a 2f 72 25 5c 1c 46 1d 8c 19 79 14 65 10 51 0c 3e 2f", + "96 29 82 21 6c 1b 56 14 42 42 a0 39 8a 2f 73 26 5c 1d 46 56", + "a9 4a 92 3d 7a 31 62 25 4a 69 b3 5b 9b 4b 81 3c 67 2e 4f 61", + "af 54 98 46 7f 38 65 2b 4e 5a ab 4e 94 41 7c 34 63 28 4c 53", + "a8 48 91 3c 7a 30 61 25 4b 53 75 97 b9 d9 f8", +] + .join(" ") + .split(" "); + +/** + * The color of each pen of `DWF Virtual Pens.ctb`, which it plots each ACI index in, from 1 to + * 255, ten to a row. A pen holds a color of its own, which is not always the index's. + */ +const VIRTUAL_PENS: readonly string[] = [ + "ff0000 ffff00 00ff00 00ffff 0000ff ff00ff 000000 808080 c0c0c0 ff0101", + "ff7f7f a50000 a55252 7f0000 7f3f3f 4c0000 4c2626 260000 261313 ff3f00", + "ff9f7f a52900 a56752 7f1f00 7f4f3f 4c1300 4c2f26 260900 261713 ff7f00", + "ffbf7f a55200 a57c52 7f3f00 7f5f3f 4c2600 4c3926 261300 261c13 ffbf00", + "ffdf7f a57c00 a59152 7f5f00 7f6f3f 4c3900 4c4226 261c00 262113 ffff01", + "ffff7f a5a500 a5a552 7f7f00 7f7f3f 4c4c00 4c4c26 262600 262613 bfff00", + "dfff7f 7ca500 91a552 5f7f00 6f7f3f 394c00 424c26 1c2600 212613 7fff00", + "bfff7f 52a500 7ca552 3f7f00 5f7f3f 264c00 394c26 132600 1c2613 3fff00", + "9fff7f 29a500 67a552 1f7f00 4f7f3f 134c00 2f4c26 092600 172613 01ff01", + "7fff7f 00a500 52a552 007f00 3f7f3f 004c00 264c26 002600 132613 00ff3f", + "7fff9f 00a529 52a567 007f1f 3f7f4f 004c13 264c2f 002609 132617 00ff7f", + "7fffbf 00a552 52a57c 007f3f 3f7f5f 004c26 264c39 002613 13261c 00ffbf", + "7fffdf 00a57c 52a591 007f5f 3f7f6f 004c39 264c42 00261c 132621 01ffff", + "7fffff 00a5a5 52a5a5 007f7f 3f7f7f 004c4c 264c4c 002626 132626 00bfff", + "7fdfff 007ca5 5291a5 005f7f 3f6f7f 00394c 26424c 001c26 132126 007fff", + "7fbfff 0052a5 527ca5 003f7f 3f5f7f 00264c 26394c 001326 131c26 003fff", + "7f9fff 0029a5 5267a5 001f7f 3f4f7f 00134c 262f4c 000926 131726 0101ff", + "7f7fff 0000a5 5252a5 00007f 3f3f7f 00004c 26264c 000026 131326 3f00ff", + "9f7fff 2900a5 6752a5 1f007f 4f3f7f 13004c 2f264c 090026 171326 7f00ff", + "bf7fff 5200a5 7c52a5 3f007f 5f3f7f 26004c 39264c 130026 1c1326 bf00ff", + "df7fff 7c00a5 9152a5 5f007f 6f3f7f 39004c 42264c 1c0026 211326 ff01ff", + "ff7fff a500a5 a552a5 7f007f 7f3f7f 4c004c 4c264c 260026 261326 ff00bf", + "ff7fdf a5007c a55291 7f005f 7f3f6f 4c0039 4c2642 26001c 261321 ff007f", + "ff7fbf a50052 a5527c 7f003f 7f3f5f 4c0026 4c2639 260013 26131c ff003f", + "ff7f9f a50029 a55267 7f001f 7f3f4f 4c0013 4c262f 260009 261317 545454", + "767676 989898 bababa dcdcdc fcfcfc", +] + .join(" ") + .split(" "); + +/** + * The color a plot style table plots an ACI index in, on paper, where index 7 is black. Under a + * table that gives each pen a color of its own, an index outside 1 to 255 plots as 7 does. + */ +function plottedColor(table: string, index: number): string { + if (table === "monochrome.ctb" || index === 7) return "#000000"; + if (table === "Grayscale.ctb") return `#${GRAYSCALE[index - 1]?.repeat(3) ?? "000000"}`; + if (table === "DWF Virtual Pens.ctb") return `#${VIRTUAL_PENS[index - 1] ?? "000000"}`; + return aciColor(index); +} + +/** The lineweight `DWF Virtual Pens.ctb` plots every pen at, in hundredths of a millimetre. */ +const VIRTUAL_PEN_WIDTH = 25.4; + /** AutoCAD draws a lineweight of "Default" at its LWDEFAULT, 0.25 mm. */ const DEFAULT_LINEWEIGHT = 25; @@ -157,9 +225,10 @@ interface Pen { * `data-viewport` that shows model space at the viewport's scale, clipped to its rectangle, * and then what is drawn on the sheet. Every line is as wide as its lineweight on the paper, * a dash is as long on the paper as its pattern times the linetype scale, in a viewport too, - * and colors are the ones the layout's plot style table plots: black for `monochrome.ctb`. A - * viewport's border is drawn as the thinnest line when its layer is on, thawed and set to - * plot, and its view is drawn whatever its layer's state. `data-entity` counts the layout's + * and colors are the ones the layout's plot style table plots: black for `monochrome.ctb`. + * `DWF Virtual Pens.ctb` plots every line 0.254 mm wide, whatever its lineweight. A + * viewport's border is drawn as the thinnest line, or 0.254 mm wide under that table, when its + * layer is on, thawed and set to plot, and its view is drawn whatever its layer's state. `data-entity` counts the layout's * entities, and inside a viewport's group the entities of model space. * * With `options.view`, the picture is that rectangle of the space: what lies well outside it is @@ -342,11 +411,12 @@ export function toSVG(drawing: Drawing, options: SvgOptions = {}): string { if (!layout || !paper) { throw new RangeError(`The drawing has no layout named ${JSON.stringify(options.layout)}`); } - // A sheet is paper: a pen plots black unless the plot style table keeps its color, and - // index 7 is black on paper. - const monochrome = (layout.plotStyle ?? DEFAULT_PLOT_STYLE_TABLE) === "monochrome.ctb"; - const color = (index: number): string => - monochrome || index === 7 ? "#000000" : aciColor(index); + // A sheet is paper: a pen plots in the color its plot style table gives it, and in its own + // lineweight unless the table gives every pen one. + const table = layout.plotStyle ?? DEFAULT_PLOT_STYLE_TABLE; + const color = (index: number): string => plottedColor(table, index); + const pen = table === "DWF Virtual Pens.ctb" ? VIRTUAL_PEN_WIDTH : undefined; + const base = pen ?? DEFAULT_LINEWEIGHT; // Hundredths of a millimetre in paper units. const perHundredth = paper.units === "in" ? 1 / 2540 : 1 / 100; /** A look whose lines are as wide as they plot, in a space `scale` paper units to one. */ @@ -355,12 +425,13 @@ export function toSVG(drawing: Drawing, options: SvgOptions = {}): string { dashScale: linetypeScale / scale, indent, stroke: (lineweight) => - lineweight === DEFAULT_LINEWEIGHT + pen !== undefined || lineweight === DEFAULT_LINEWEIGHT ? "" : ` stroke-width="${precise((Math.max(lineweight, 5) * perHundredth) / scale)}"`, }); const onPaper = plotted(1, " "); - const thin = fmt(5 * perHundredth); + // A viewport's border plots as the thinnest line, or as the one pen a table gives every line. + const thin = fmt((pen ?? 5) * perHundredth); // A drawing in units tsdraft does not know is drawn as one in inches, as `lint` judges it. const units = isUnits(drawing.units) ? drawing.units : "in"; // The picture: the paper, or the view asked for, and the margin around it, in paper units @@ -446,7 +517,7 @@ export function toSVG(drawing: Drawing, options: SvgOptions = {}): string { [ ``, ` `, - ` `, + ` `, ...inside.map((element) => ` ${element}`), ` `, ` `, @@ -465,7 +536,7 @@ export function toSVG(drawing: Drawing, options: SvgOptions = {}): string { return [ ``, ` `, - ` `, + ` `, ...parts.map((part) => ` ${part}`), ` `, ``, diff --git a/standard.schema.json b/standard.schema.json index 8ac5234..39bbb9b 100644 --- a/standard.schema.json +++ b/standard.schema.json @@ -50,7 +50,7 @@ }, "plotStyle": { "type": "string", - "enum": ["monochrome.ctb", "acad.ctb"], + "enum": ["monochrome.ctb", "acad.ctb", "Grayscale.ctb", "DWF Virtual Pens.ctb"], "description": "The plot style table of each layout. One of PLOT_STYLE_TABLES", "default": "monochrome.ctb" }, @@ -551,7 +551,17 @@ }, "paper": { "type": "string", - "enum": ["ARCH A", "ARCH B", "ARCH C", "ARCH D", "ANSI A", "ANSI B", "ANSI C", "ANSI D"], + "enum": [ + "ARCH A", + "ARCH B", + "ARCH C", + "ARCH D", + "ARCH E", + "ANSI A", + "ANSI B", + "ANSI C", + "ANSI D" + ], "description": "One of PAPERS, which is its title block's paper" }, "titleBlock": { @@ -560,7 +570,7 @@ }, "plotStyle": { "type": "string", - "enum": ["monochrome.ctb", "acad.ctb"], + "enum": ["monochrome.ctb", "acad.ctb", "Grayscale.ctb", "DWF Virtual Pens.ctb"], "description": "The sheet format's own plot style table, in place of the standard's" }, "zones": { @@ -735,7 +745,17 @@ }, "paper": { "type": "string", - "enum": ["ARCH A", "ARCH B", "ARCH C", "ARCH D", "ANSI A", "ANSI B", "ANSI C", "ANSI D"], + "enum": [ + "ARCH A", + "ARCH B", + "ARCH C", + "ARCH D", + "ARCH E", + "ANSI A", + "ANSI B", + "ANSI C", + "ANSI D" + ], "description": "One of PAPERS" }, "block": { diff --git a/test/layouts.test.ts b/test/layouts.test.ts index ffc7075..90d91e9 100644 --- a/test/layouts.test.ts +++ b/test/layouts.test.ts @@ -14,6 +14,7 @@ import { viewportView, type Drawing, type Layout, + type PlotStyleTable, } from "../src/index.ts"; import { layoutNameKey } from "../src/layers.ts"; import { findLayout } from "../src/layouts.ts"; @@ -243,6 +244,20 @@ test("a second layout is a block of its own, with what it holds inside it", () = assert.ok(dxf.includes("acad.ctb")); }); +test("an ARCH E layout is 48 by 36, with its plot turned onto the upright medium", () => { + const d = createDrawing({ units: "ft" }); + d.layout("E-101", { paper: "ARCH E" }, () => d.line([0, 0], [1, 1])); + const dxf = toDXF(d.build()); + const setup = dxf.slice(dxf.indexOf("ARCH_full_bleed_E_(36.00_x_48.00_Inches)")); + // The medium is 36 wide and 48 tall, in millimetres as AutoCAD holds it, and AutoCAD's own + // landscape page setup on it turns the plot with a rotation of 1. + assert.match(setup, /\r\n44\r\n914\.400024\r\n45\r\n1219\.199951\r\n/); + assert.match(setup, /\r\n72\r\n0\r\n73\r\n1\r\n/); + // The layout's limits are 48 by 36. + assert.match(setup, /\r\n11\r\n47\.999998\r\n21\r\n36\.000001\r\n/); + assert.deepEqual([PAPERS["ARCH E"].width, PAPERS["ARCH E"].height], [48, 36]); +}); + test("a layout's name is held to AutoCAD's rules, at compile time and when the code runs", () => { const d = createDrawing(); const refused = (run: () => unknown, pattern: RegExp): void => { @@ -501,6 +516,17 @@ test("toSVG draws a layout as it plots: the sheet, each view clipped at its scal ); assert.match(colored, /stroke="#ff0000" stroke-width="0.196850393701" [^>]*data-id="wall"/); assert.match(colored, /stroke="#000000" data-layer="0" data-entity="0" data-id="border"/); + // Grayscale.ctb plots red in a gray of its own, and DWF Virtual Pens.ctb plots every pen + // 0.254 mm wide. + const plotted = (plotStyle: PlotStyleTable): string => + toSVG({ ...drawing, layouts: [{ ...drawing.layouts![0]!, plotStyle }] }, { layout: "A-101" }); + assert.match( + plotted("Grayscale.ctb"), + /stroke="#4c4c4c" stroke-width="0.196850393701" [^>]*data-id="wall"/, + ); + const pens = plotted("DWF Virtual Pens.ctb"); + assert.match(pens, /fill="none" stroke-width="0.01">/); + assert.match(pens, /stroke="#ff0000" data-layer="WALL" [^>]*data-id="wall"/); assert.throws(() => toSVG(drawing, { layout: "Nope" }), RangeError); // Model space is drawn as it was. assert.doesNotMatch(toSVG(drawing), /data-layout|border/);