postext 1.59.1 → 1.60.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/__tests__/exercises/digital.test.js +19 -1
  2. package/dist/__tests__/exercises/digital.test.js.map +1 -1
  3. package/dist/__tests__/exercises/followups.test.d.ts +2 -0
  4. package/dist/__tests__/exercises/followups.test.d.ts.map +1 -0
  5. package/dist/__tests__/exercises/followups.test.js +235 -0
  6. package/dist/__tests__/exercises/followups.test.js.map +1 -0
  7. package/dist/__tests__/exercises/layout.test.js +31 -0
  8. package/dist/__tests__/exercises/layout.test.js.map +1 -1
  9. package/dist/__tests__/pipeline/spanHeadingMidPage.test.js +76 -0
  10. package/dist/__tests__/pipeline/spanHeadingMidPage.test.js.map +1 -1
  11. package/dist/canvas-backend/annotations.d.ts +4 -1
  12. package/dist/canvas-backend/annotations.d.ts.map +1 -1
  13. package/dist/canvas-backend/annotations.js +5 -0
  14. package/dist/canvas-backend/annotations.js.map +1 -1
  15. package/dist/canvas-backend/chip.d.ts.map +1 -1
  16. package/dist/canvas-backend/chip.js +4 -0
  17. package/dist/canvas-backend/chip.js.map +1 -1
  18. package/dist/canvas-backend/swatch.js +1 -1
  19. package/dist/canvas-backend/swatch.js.map +1 -1
  20. package/dist/defaults/exercises.d.ts.map +1 -1
  21. package/dist/defaults/exercises.js +15 -9
  22. package/dist/defaults/exercises.js.map +1 -1
  23. package/dist/exercises/build.d.ts.map +1 -1
  24. package/dist/exercises/build.js +1 -0
  25. package/dist/exercises/build.js.map +1 -1
  26. package/dist/exercises/configWarnings.d.ts.map +1 -1
  27. package/dist/exercises/configWarnings.js +6 -2
  28. package/dist/exercises/configWarnings.js.map +1 -1
  29. package/dist/exercises/designs.d.ts +5 -0
  30. package/dist/exercises/designs.d.ts.map +1 -1
  31. package/dist/exercises/designs.js +45 -16
  32. package/dist/exercises/designs.js.map +1 -1
  33. package/dist/exercises/expand.d.ts +2 -0
  34. package/dist/exercises/expand.d.ts.map +1 -1
  35. package/dist/exercises/expand.js +63 -19
  36. package/dist/exercises/expand.js.map +1 -1
  37. package/dist/exercises/grids.d.ts.map +1 -1
  38. package/dist/exercises/grids.js +76 -16
  39. package/dist/exercises/grids.js.map +1 -1
  40. package/dist/exercises/model.d.ts +4 -1
  41. package/dist/exercises/model.d.ts.map +1 -1
  42. package/dist/exercises/model.js +56 -15
  43. package/dist/exercises/model.js.map +1 -1
  44. package/dist/exercises/record.d.ts +5 -1
  45. package/dist/exercises/record.d.ts.map +1 -1
  46. package/dist/exercises/runtime.d.ts.map +1 -1
  47. package/dist/exercises/runtime.js +61 -13
  48. package/dist/exercises/runtime.js.map +1 -1
  49. package/dist/exercises/styles.d.ts +8 -0
  50. package/dist/exercises/styles.d.ts.map +1 -1
  51. package/dist/exercises/styles.js +16 -2
  52. package/dist/exercises/styles.js.map +1 -1
  53. package/dist/html-backend.d.ts.map +1 -1
  54. package/dist/html-backend.js +35 -23
  55. package/dist/html-backend.js.map +1 -1
  56. package/dist/htmlAnnotations.d.ts +5 -1
  57. package/dist/htmlAnnotations.d.ts.map +1 -1
  58. package/dist/htmlAnnotations.js +6 -0
  59. package/dist/htmlAnnotations.js.map +1 -1
  60. package/dist/measure/cache.js +3 -3
  61. package/dist/measure/cache.js.map +1 -1
  62. package/dist/measure/inlineIcon.d.ts +5 -2
  63. package/dist/measure/inlineIcon.d.ts.map +1 -1
  64. package/dist/measure/inlineIcon.js +8 -4
  65. package/dist/measure/inlineIcon.js.map +1 -1
  66. package/dist/measure/rich.d.ts.map +1 -1
  67. package/dist/measure/rich.js +72 -4
  68. package/dist/measure/rich.js.map +1 -1
  69. package/dist/parse/types.d.ts +7 -0
  70. package/dist/parse/types.d.ts.map +1 -1
  71. package/dist/pipeline/build.d.ts.map +1 -1
  72. package/dist/pipeline/build.js +38 -7
  73. package/dist/pipeline/build.js.map +1 -1
  74. package/dist/types.d.ts +18 -1
  75. package/dist/types.d.ts.map +1 -1
  76. package/dist/vdt.d.ts +8 -0
  77. package/dist/vdt.d.ts.map +1 -1
  78. package/dist/vdt.js.map +1 -1
  79. package/package.json +1 -1
@@ -20,7 +20,7 @@ import { measureFontBox, measureSpacedWidth, measureTextWidth, measuresOwnFace,
20
20
  import { trackedLigaturePieces } from './measure/trackedLigatures.js';
21
21
  import { cellsOf, lineWordSegmentsInOrder, sameSetWidth } from './wordPieces.js';
22
22
  import { fontFamilyOf } from './measure/vertical.js';
23
- import { lineHighlightsHtml, lineMarksHtml, rubyHtml, verticalLineMarksHtml, warichuHtml, sideMarkerHtml, RT_OPEN, RUBY_OPEN, rubyGroupOf, kuntenHtml, verticalKuntenTateHtml } from './htmlAnnotations.js';
23
+ import { chipReadingsHtml, lineHighlightsHtml, lineMarksHtml, rubyHtml, verticalLineMarksHtml, warichuHtml, sideMarkerHtml, RT_OPEN, RUBY_OPEN, rubyGroupOf, kuntenHtml, verticalKuntenTateHtml } from './htmlAnnotations.js';
24
24
  import { playMarkTriangle, qrModuleRuns } from './pipeline/videoOverlay.js';
25
25
  import { isHlsMimeType, mediaFragment, videoElementAttributes, videoEmbedAllow } from './video/url.js';
26
26
  import { hasSwatchShape, swatchGeometry } from './swatchShape.js';
@@ -1310,9 +1310,11 @@ function cjkAt(char) {
1310
1310
  */
1311
1311
  function renderSegments(line, block, targets, rootDir = 'ltr', rootLang,
1312
1312
  /** What a copy puts after the line (see {@link lineEndText}). */
1313
- end = '') {
1313
+ end = '',
1314
+ /** Where the line's inline pictures come from (#1249). */
1315
+ paint) {
1314
1316
  if (line.cjkComposed)
1315
- return renderComposedSegments(line, block, targets, rootDir, end);
1317
+ return renderComposedSegments(line, block, targets, rootDir, end, paint);
1316
1318
  // The tracking after the last glyph is advance, not ink: centring and
1317
1319
  // right alignment leave it out (EF-153), as the canvas does.
1318
1320
  const tracking = (block.letterSpacing ?? 0) + (line.letterSpacing ?? 0);
@@ -1410,7 +1412,7 @@ end = '') {
1410
1412
  continue;
1411
1413
  }
1412
1414
  if (seg.kind === 'swatch') {
1413
- parts.push(renderSwatchSegment(seg.swatch, x, line.baseline - line.bbox.y, seg.width, block.color, undefined, seg.rtl === true));
1415
+ parts.push(renderSwatchSegment(seg.swatch, x, line.baseline - line.bbox.y, seg.width, block.color, paint, seg.rtl === true));
1414
1416
  x += seg.width;
1415
1417
  continue;
1416
1418
  }
@@ -1463,7 +1465,7 @@ function orderedOffsets(segs, order, start, spaceWidth) {
1463
1465
  /** {@link renderSegments} for a line of the CJK composer: hung marks and
1464
1466
  * Han–Latin spaces kept out of the justification, each segment with its
1465
1467
  * own tracking, ink offset and scale, warichu notes and ruby readings. */
1466
- function renderComposedSegments(line, block, targets, rootDir = 'ltr', end = '') {
1468
+ function renderComposedSegments(line, block, targets, rootDir = 'ltr', end = '', paint) {
1467
1469
  // The tracking after the last glyph is advance, not ink: centring and
1468
1470
  // right alignment leave it out (EF-153), as the canvas does.
1469
1471
  const lineTracking = (block.letterSpacing ?? 0) + (line.letterSpacing ?? 0);
@@ -1564,7 +1566,7 @@ function renderComposedSegments(line, block, targets, rootDir = 'ltr', end = '')
1564
1566
  continue;
1565
1567
  }
1566
1568
  if (seg.kind === 'swatch') {
1567
- parts.push(renderSwatchSegment(seg.swatch, x, line.baseline - line.bbox.y, seg.width, block.color, undefined, seg.rtl === true));
1569
+ parts.push(renderSwatchSegment(seg.swatch, x, line.baseline - line.bbox.y, seg.width, block.color, paint, seg.rtl === true));
1568
1570
  x += seg.width;
1569
1571
  continue;
1570
1572
  }
@@ -1784,6 +1786,9 @@ rootDir = 'ltr') {
1784
1786
  const label = chip.blank ? ` role="img" aria-label="${esc(chip.blankLabel ?? 'blank')}"` : '';
1785
1787
  parts.push(`<span class="pt-ex-slot${chip.blank ? ' pt-ex-blank' : ''}"${data}${label} style="${boxGeometry}"></span>`);
1786
1788
  }
1789
+ // The readings of its ruby words, over a blank too (#1244).
1790
+ if (chip.readings && chip.runs[0])
1791
+ parts.push(chipReadingsHtml(chip.readings, bx, inkFor(chip.runs[0]), quoteFontString));
1787
1792
  if (chip.blank)
1788
1793
  return parts.join('');
1789
1794
  let tx = bx + chip.borderWidth + chip.paddingX;
@@ -1931,7 +1936,9 @@ function renderLine(line, block, targets, rootDir = 'ltr', rootLang,
1931
1936
  next,
1932
1937
  /** Markup set at the start of the line's box, before its text (a drop
1933
1938
  * cap, #623). */
1934
- lead = '') {
1939
+ lead = '',
1940
+ /** Where the line's inline pictures come from (#1249). */
1941
+ paint) {
1935
1942
  const font = quoteFontString(block.fontString);
1936
1943
  const strikethroughDecl = block.strikethroughText ? 'text-decoration:line-through;' : '';
1937
1944
  // Tracking — the block's (column balancing, a runt set short) and the
@@ -1951,7 +1958,7 @@ lead = '') {
1951
1958
  strikethroughDecl +
1952
1959
  trackingDecl +
1953
1960
  (lineCjkDecl(line) ? cjkTextDecl(tracking !== 0) : '') +
1954
- `">${lineHighlightsHtml(line)}${lead}${renderSegments(line, block, targets, rootDir, rootLang, lineEndText(line, next))}</div>`);
1961
+ `">${lineHighlightsHtml(line)}${lead}${renderSegments(line, block, targets, rootDir, rootLang, lineEndText(line, next), paint)}</div>`);
1955
1962
  }
1956
1963
  /**
1957
1964
  * A paragraph's drop cap (#623), as the first runs of its first line's box:
@@ -2141,7 +2148,7 @@ function verticalWordSegments(line, block, v, tracking) {
2141
2148
  /** A body line of a vertical page: its text segments down an upright box,
2142
2149
  * its formulas, swatches and chips sideways where the canvas paints them
2143
2150
  * (see the section comment). */
2144
- function renderVerticalLine(line, block, v, targets, next) {
2151
+ function renderVerticalLine(line, block, v, targets, next, paint) {
2145
2152
  const lineTracking = (block.letterSpacing ?? 0) + (line.letterSpacing ?? 0);
2146
2153
  const trailing = lineTrailingTracking(line, lineTracking);
2147
2154
  const lineIndent = line.bbox.x - block.bbox.x;
@@ -2190,7 +2197,7 @@ function renderVerticalLine(line, block, v, targets, next) {
2190
2197
  continue;
2191
2198
  }
2192
2199
  if (seg.kind === 'swatch') {
2193
- sideways.push(renderSwatchSegment(seg.swatch, x, line.baseline - line.bbox.y, seg.width, block.color, undefined, seg.rtl === true));
2200
+ sideways.push(renderSwatchSegment(seg.swatch, x, line.baseline - line.bbox.y, seg.width, block.color, paint, seg.rtl === true));
2194
2201
  x += seg.width;
2195
2202
  continue;
2196
2203
  }
@@ -2724,7 +2731,7 @@ function renderFigureLabelsHtml(rb, bx, by, options) {
2724
2731
  const n2 = (v) => String(+v.toFixed(4));
2725
2732
  // Their leaders first (#1057), under every label: vector lines hidden
2726
2733
  // from assistive technology, as design paths are.
2727
- const parts = figureLabelLeaderPaths(rb, bx, by).map(renderDesignPathBlock);
2734
+ const parts = figureLabelLeaderPaths(rb, bx, by).map((p) => renderDesignPathBlock(p));
2728
2735
  for (const label of rb.labels ?? []) {
2729
2736
  let layout = label.layout;
2730
2737
  if (!layout)
@@ -2760,7 +2767,7 @@ function renderFigureLabelsHtml(rb, bx, by, options) {
2760
2767
  id: label.id ?? '', fontString: layout.fontString, boldFontString: layout.boldFontString, italicFontString: layout.italicFontString,
2761
2768
  boldItalicFontString: layout.boldItalicFontString, color: layout.color, textAlign: 'left', bbox: layout.box,
2762
2769
  };
2763
- layout.lines.forEach((line, i) => inner.push(renderVerticalLine(line, host, v, options.linkTargets, layout.lines[i + 1])));
2770
+ layout.lines.forEach((line, i) => inner.push(renderVerticalLine(line, host, v, options.linkTargets, layout.lines[i + 1], options)));
2764
2771
  }
2765
2772
  else {
2766
2773
  layout.lines.forEach((line, i) => {
@@ -3095,7 +3102,7 @@ function renderDesignRuleBlock(block) {
3095
3102
  * box like a rule, its outline and markers drawn in the box's own
3096
3103
  * coordinates (ink past the box, a mitre, still shows). Decoration:
3097
3104
  * hidden from assistive technology. */
3098
- function renderDesignPathBlock(block) {
3105
+ function renderDesignPathBlock(block, mirrored = false) {
3099
3106
  const { x, y, width, height } = block.bbox;
3100
3107
  const parts = [];
3101
3108
  const stroke = block.stroke && block.stroke.widthPx > 0 ? block.stroke : undefined;
@@ -3118,7 +3125,7 @@ function renderDesignPathBlock(block) {
3118
3125
  parts.push(markerSvgElement(m, x, y, esc));
3119
3126
  if (parts.length === 0)
3120
3127
  return '';
3121
- return (`<svg aria-hidden="true" focusable="false" width="${width}" height="${height}" ` +
3128
+ return (`<svg${mirrored ? ' class="pt-path"' : ''} aria-hidden="true" focusable="false" width="${width}" height="${height}" ` +
3122
3129
  `style="position:absolute;left:${x}px;top:${y}px;width:${width}px;height:${height}px;overflow:visible;">` +
3123
3130
  parts.join('') +
3124
3131
  `</svg>`);
@@ -3177,7 +3184,7 @@ function renderDesignImageBlock(block, options) {
3177
3184
  `background:rgba(160,160,160,0.12);border:1px solid rgba(160,160,160,0.5);box-sizing:border-box;` +
3178
3185
  `"></div>`);
3179
3186
  }
3180
- function renderDesignBlock(block, options) {
3187
+ function renderDesignBlock(block, options, mirroredPaths = false) {
3181
3188
  if (block.kind === 'text')
3182
3189
  return renderDesignTextBlock(block, options);
3183
3190
  if (block.kind === 'rule')
@@ -3185,20 +3192,23 @@ function renderDesignBlock(block, options) {
3185
3192
  if (block.kind === 'image')
3186
3193
  return renderDesignImageBlock(block, options);
3187
3194
  if (block.kind === 'path')
3188
- return renderDesignPathBlock(block);
3195
+ return renderDesignPathBlock(block, mirroredPaths);
3189
3196
  return renderDesignBoxBlock(block);
3190
3197
  }
3191
- function renderDesignSlot(slot, options) {
3198
+ function renderDesignSlot(slot, options, mirroredPaths = false) {
3192
3199
  const parts = [];
3193
3200
  for (const block of slot.blocks)
3194
- parts.push(renderDesignBlock(block, options));
3201
+ parts.push(renderDesignBlock(block, options, mirroredPaths));
3195
3202
  return parts.join('');
3196
3203
  }
3197
3204
  function renderBlockInner(block, options) {
3198
3205
  if (block.hidden)
3199
3206
  return '';
3207
+ // An exercise's drawn box (a grid, an operation, a writing area) keeps
3208
+ // its paths mirrored with a mirrored flow, as the canvas and the PDF
3209
+ // paint it (#1244).
3200
3210
  if (block.designOverlay)
3201
- return renderDesignSlot(block.designOverlay, options);
3211
+ return renderDesignSlot(block.designOverlay, options, !!block.callout?.styleId?.startsWith('__postext-ex'));
3202
3212
  // Resource embeds carry their own measured geometry (image/table + caption);
3203
3213
  // the block's single placeholder line renders nothing useful.
3204
3214
  if (block.resourceBlock)
@@ -3213,7 +3223,7 @@ function renderBlockInner(block, options) {
3213
3223
  block.lines.forEach((line, i) => {
3214
3224
  const next = block.lines[i + 1];
3215
3225
  const lead = i === 0 && block.dropCap && !v ? dropCapHtml(block.dropCap, line, quoteFontString(block.fontString), (block.letterSpacing ?? 0) + (line.letterSpacing ?? 0)) : '';
3216
- parts.push(v ? renderVerticalLine(line, block, v, options.linkTargets, next) : renderLine(line, block, options.linkTargets, options.dir, options.rootLang, next, lead));
3226
+ parts.push(v ? renderVerticalLine(line, block, v, options.linkTargets, next, options) : renderLine(line, block, options.linkTargets, options.dir, options.rootLang, next, lead, options));
3217
3227
  });
3218
3228
  return parts.join('');
3219
3229
  }
@@ -3237,7 +3247,7 @@ function renderStripCaptionHtml(block, options) {
3237
3247
  const lines = block.lines.slice(caption.firstLine, caption.firstLine + caption.lineCount);
3238
3248
  lines.forEach((line, i) => {
3239
3249
  const next = lines[i + 1];
3240
- parts.push(v ? renderVerticalLine(line, block, v, options.linkTargets, next) : renderLine(line, block, options.linkTargets, options.dir, options.rootLang, next));
3250
+ parts.push(v ? renderVerticalLine(line, block, v, options.linkTargets, next, options) : renderLine(line, block, options.linkTargets, options.dir, options.rootLang, next, '', options));
3241
3251
  });
3242
3252
  return parts.join('');
3243
3253
  }
@@ -3586,12 +3596,14 @@ function renderComicPageHtml(page, comic, k, options, overPanel) {
3586
3596
  * mirrored layout put it. A box that sets a transform of its own (a
3587
3597
  * stretched dash, a turned design picture) keeps it and stays mirrored. */
3588
3598
  const MIRRORED_FLOW_STYLE = '<style>.pt-flow-mirrored [style*="white-space:pre"],.pt-flow-mirrored img,' +
3589
- '.pt-flow-mirrored svg:not(.pt-char-grid),.pt-flow-mirrored .pt-video{transform:scaleX(-1);}' +
3599
+ // An exercise's drawing is geometry, mirrored with the flow as the canvas
3600
+ // and the PDF mirror it (#1244): a crossword's cells, an operation's rules.
3601
+ '.pt-flow-mirrored svg:not(.pt-char-grid):not(.pt-path),.pt-flow-mirrored .pt-video{transform:scaleX(-1);}' +
3590
3602
  // A box reflected back about its body (a vertical label, #955) or a
3591
3603
  // vertical frame of the page placed by a matrix that includes the mirror
3592
3604
  // (#960): what is in it is not mirrored, so nothing is turned back.
3593
3605
  '.pt-flow-mirrored .pt-unmirrored [style*="white-space:pre"],.pt-flow-mirrored .pt-unmirrored img,' +
3594
- '.pt-flow-mirrored .pt-unmirrored svg:not(.pt-char-grid),.pt-flow-mirrored .pt-unmirrored .pt-video{transform:none;}</style>';
3606
+ '.pt-flow-mirrored .pt-unmirrored svg:not(.pt-char-grid):not(.pt-path),.pt-flow-mirrored .pt-unmirrored .pt-video{transform:none;}</style>';
3595
3607
  /** The character grid (稿纸) as one SVG path over the page, under the
3596
3608
  * text (see `cjkGridCells`). */
3597
3609
  function renderCharacterGridSvg(cells, width, height) {