kviewer 0.1.1 → 0.2.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 (51) hide show
  1. package/README.md +12 -0
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +26 -2
  4. package/dist/runtime/annotation/engine/config.d.ts +1 -1
  5. package/dist/runtime/annotation/engine/config.js +2 -2
  6. package/dist/runtime/annotation/engine/painter.d.ts +1 -1
  7. package/dist/runtime/annotation/engine/utils.d.ts +2 -1
  8. package/dist/runtime/annotation/engine/utils.js +3 -7
  9. package/dist/runtime/annotation/pdf-export/export.js +63 -9
  10. package/dist/runtime/annotation/pdf-export/parse_circle.js +3 -2
  11. package/dist/runtime/annotation/pdf-export/parse_freetext.js +18 -8
  12. package/dist/runtime/annotation/pdf-export/parse_highlight.js +27 -29
  13. package/dist/runtime/annotation/pdf-export/parse_ink.js +5 -3
  14. package/dist/runtime/annotation/pdf-export/parse_line.js +5 -3
  15. package/dist/runtime/annotation/pdf-export/parse_polyline.js +6 -4
  16. package/dist/runtime/annotation/pdf-export/parse_square.js +3 -2
  17. package/dist/runtime/annotation/pdf-export/parse_stamp.js +16 -9
  18. package/dist/runtime/annotation/pdf-export/parse_strikeout.js +11 -10
  19. package/dist/runtime/annotation/pdf-export/parse_text.js +3 -1
  20. package/dist/runtime/annotation/pdf-export/parse_underline.js +11 -10
  21. package/dist/runtime/annotation/pdf-export/parse_utils.d.ts +11 -0
  22. package/dist/runtime/annotation/pdf-export/parse_utils.js +12 -0
  23. package/dist/runtime/annotation/pdf-import/decode.d.ts +2 -1
  24. package/dist/runtime/annotation/pdf-import/decode.js +6 -5
  25. package/dist/runtime/annotation/pdf-import/decode_circle.js +1 -1
  26. package/dist/runtime/annotation/pdf-import/decode_freetext.js +1 -1
  27. package/dist/runtime/annotation/pdf-import/decode_highlight.js +1 -1
  28. package/dist/runtime/annotation/pdf-import/decode_ink.js +1 -1
  29. package/dist/runtime/annotation/pdf-import/decode_line.js +1 -1
  30. package/dist/runtime/annotation/pdf-import/decode_square.js +1 -1
  31. package/dist/runtime/annotation/pdf-import/decode_stamp.js +1 -1
  32. package/dist/runtime/annotation/pdf-import/decode_text.js +1 -1
  33. package/dist/runtime/annotation/pdf-import/extract_stamp_appearance.d.ts +3 -2
  34. package/dist/runtime/annotation/pdf-import/extract_stamp_appearance.js +7 -15
  35. package/dist/runtime/annotation/pdf-import/transform.d.ts +85 -0
  36. package/dist/runtime/annotation/pdf-import/transform.js +81 -0
  37. package/dist/runtime/annotation/pdf-import/types.d.ts +5 -1
  38. package/dist/runtime/annotation/pdf-import/utils.d.ts +6 -5
  39. package/dist/runtime/annotation/pdf-import/utils.js +53 -38
  40. package/dist/runtime/components/AnnotationToolbar.vue +16 -0
  41. package/dist/runtime/components/FormFieldLayer.d.vue.ts +4 -7
  42. package/dist/runtime/components/FormFieldLayer.vue +5 -10
  43. package/dist/runtime/components/FormFieldLayer.vue.d.ts +4 -7
  44. package/dist/runtime/components/PdfPage.vue +1 -6
  45. package/dist/runtime/components/form-fields/FormFieldWrapper.d.vue.ts +5 -6
  46. package/dist/runtime/components/form-fields/FormFieldWrapper.vue +8 -14
  47. package/dist/runtime/components/form-fields/FormFieldWrapper.vue.d.ts +5 -6
  48. package/dist/runtime/composables/usePageVirtualization.d.ts +6 -0
  49. package/dist/runtime/composables/usePageVirtualization.js +8 -2
  50. package/dist/runtime/style.css +1 -0
  51. package/package.json +5 -3
@@ -1,6 +1,7 @@
1
1
  import Konva from 'konva';
2
2
  import { type AnnotationType, type IAnnotationComment, type IAnnotationStore, type PdfjsAnnotationSubtype } from '../engine/types.js';
3
3
  import { type RawPdfAnnotation } from './types.js';
4
+ import { type PageTransform } from './transform.js';
4
5
  type Rect = {
5
6
  x: number;
6
7
  y: number;
@@ -17,8 +18,8 @@ export declare function getAnnotationText(annotation: RawPdfAnnotation): string;
17
18
  export declare function getFreeTextContent(annotation: RawPdfAnnotation): string;
18
19
  export declare function getAnnotationDate(annotation: RawPdfAnnotation): string;
19
20
  export declare function getComments(annotation: RawPdfAnnotation, allAnnotations: RawPdfAnnotation[]): IAnnotationComment[];
20
- export declare function rectFromPdfRect(input: unknown, pageHeight: number): Rect | null;
21
- export declare function pointFromPdfPoint(input: unknown, pageHeight: number): {
21
+ export declare function rectFromPdfRect(input: unknown, transform: PageTransform): Rect | null;
22
+ export declare function pointFromPdfPoint(input: unknown, transform: PageTransform): {
22
23
  x: number;
23
24
  y: number;
24
25
  } | null;
@@ -26,9 +27,9 @@ export declare function localPointFromPdfPoint(input: unknown): {
26
27
  x: number;
27
28
  y: number;
28
29
  } | null;
29
- export declare function rectFromQuadPoints(input: unknown, pageHeight: number): Rect | null;
30
- export declare function pointsFromInkList(input: unknown, pageHeight: number): number[];
31
- export declare function linePointsFromCoordinates(input: unknown, pageHeight: number): number[] | null;
30
+ export declare function rectFromQuadPoints(input: unknown, transform: PageTransform): Rect | null;
31
+ export declare function pointsFromInkList(input: unknown, transform: PageTransform): number[];
32
+ export declare function linePointsFromCoordinates(input: unknown, transform: PageTransform): number[] | null;
32
33
  export declare function normalizedStrokeWidth(annotation: RawPdfAnnotation, fallback?: number, options?: StrokeWidthOptions): number;
33
34
  export declare function dashedBorder(annotation: RawPdfAnnotation): number[];
34
35
  export declare function hasArrowEnding(value: unknown): boolean;
@@ -14,6 +14,10 @@ import {
14
14
  isRawPdfAnnotation,
15
15
  isString
16
16
  } from "./types.js";
17
+ import {
18
+ convertPdfPointToCanvas,
19
+ convertPdfRectToCanvas
20
+ } from "./transform.js";
17
21
  const UNKNOWN_USER = "Unknown user";
18
22
  function clampColor(value) {
19
23
  if (!Number.isFinite(value)) return 0;
@@ -108,23 +112,18 @@ export function getComments(annotation, allAnnotations) {
108
112
  }
109
113
  return comments;
110
114
  }
111
- export function rectFromPdfRect(input, pageHeight) {
115
+ export function rectFromPdfRect(input, transform) {
112
116
  if (!Array.isArray(input) || input.length < 4) return null;
113
117
  const [x1, y1, x2, y2] = input;
114
118
  if (!isNumber(x1) || !isNumber(y1) || !isNumber(x2) || !isNumber(y2)) return null;
115
- return {
116
- x: x1,
117
- y: pageHeight - y2,
118
- width: x2 - x1,
119
- height: y2 - y1
120
- };
119
+ return convertPdfRectToCanvas(transform, [x1, y1, x2, y2]);
121
120
  }
122
- export function pointFromPdfPoint(input, pageHeight) {
121
+ export function pointFromPdfPoint(input, transform) {
123
122
  const pair = toNumericTuple(input, 2);
124
- if (pair) return { x: pair[0], y: pageHeight - pair[1] };
123
+ if (pair) return convertPdfPointToCanvas(transform, pair[0], pair[1]);
125
124
  const point = asPoint(input);
126
125
  if (!point || !isNumber(point.x) || !isNumber(point.y)) return null;
127
- return { x: point.x, y: pageHeight - point.y };
126
+ return convertPdfPointToCanvas(transform, point.x, point.y);
128
127
  }
129
128
  export function localPointFromPdfPoint(input) {
130
129
  const pair = toNumericTuple(input, 2);
@@ -133,55 +132,71 @@ export function localPointFromPdfPoint(input) {
133
132
  if (!point || !isNumber(point.x) || !isNumber(point.y)) return null;
134
133
  return { x: point.x, y: point.y };
135
134
  }
136
- export function rectFromQuadPoints(input, pageHeight) {
135
+ function rawPdfPoint(input) {
136
+ const pair = toNumericTuple(input, 2);
137
+ if (pair) return [pair[0], pair[1]];
138
+ const point = asPoint(input);
139
+ if (point && isNumber(point.x) && isNumber(point.y)) return [point.x, point.y];
140
+ return null;
141
+ }
142
+ export function rectFromQuadPoints(input, transform) {
143
+ let raw = null;
137
144
  const numeric = toCoordinateList(input);
138
145
  if (numeric && numeric.length >= 8) {
139
- const p02 = pointFromPdfPoint([numeric[0], numeric[1]], pageHeight);
140
- const p12 = pointFromPdfPoint([numeric[2], numeric[3]], pageHeight);
141
- const p32 = pointFromPdfPoint([numeric[6], numeric[7]], pageHeight);
142
- if (!p02 || !p12 || !p32) return null;
143
- return {
144
- x: p02.x,
145
- y: p02.y,
146
- width: p12.x - p02.x,
147
- height: p12.y - p32.y
148
- };
146
+ raw = [
147
+ [numeric[0], numeric[1]],
148
+ [numeric[2], numeric[3]],
149
+ [numeric[4], numeric[5]],
150
+ [numeric[6], numeric[7]]
151
+ ];
152
+ } else if (Array.isArray(input) && input.length >= 4) {
153
+ const collected = [];
154
+ for (let i = 0; i < 4; i++) {
155
+ const p = rawPdfPoint(input[i]);
156
+ if (!p) return null;
157
+ collected.push(p);
158
+ }
159
+ raw = collected;
149
160
  }
150
- if (!Array.isArray(input) || input.length < 4) return null;
151
- const p0 = pointFromPdfPoint(input[0], pageHeight);
152
- const p1 = pointFromPdfPoint(input[1], pageHeight);
153
- const p3 = pointFromPdfPoint(input[3], pageHeight);
154
- if (!p0 || !p1 || !p3) return null;
155
- return {
156
- x: p0.x,
157
- y: p0.y,
158
- width: p1.x - p0.x,
159
- height: p1.y - p3.y
160
- };
161
+ if (!raw) return null;
162
+ const xs = [];
163
+ const ys = [];
164
+ for (const [rx, ry] of raw) {
165
+ const p = convertPdfPointToCanvas(transform, rx, ry);
166
+ xs.push(p.x);
167
+ ys.push(p.y);
168
+ }
169
+ const minX = Math.min(...xs);
170
+ const maxX = Math.max(...xs);
171
+ const minY = Math.min(...ys);
172
+ const maxY = Math.max(...ys);
173
+ return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
161
174
  }
162
- export function pointsFromInkList(input, pageHeight) {
175
+ export function pointsFromInkList(input, transform) {
163
176
  const numeric = toCoordinateList(input);
164
177
  if (numeric) {
165
178
  const points2 = [];
166
179
  for (let index = 0; index + 1 < numeric.length; index += 2) {
167
- points2.push(numeric[index], pageHeight - numeric[index + 1]);
180
+ const p = convertPdfPointToCanvas(transform, numeric[index], numeric[index + 1]);
181
+ points2.push(p.x, p.y);
168
182
  }
169
183
  return points2;
170
184
  }
171
185
  if (!Array.isArray(input)) return [];
172
186
  const points = [];
173
187
  for (const item of input) {
174
- const converted = pointFromPdfPoint(item, pageHeight);
188
+ const converted = pointFromPdfPoint(item, transform);
175
189
  if (!converted) continue;
176
190
  points.push(converted.x, converted.y);
177
191
  }
178
192
  return points;
179
193
  }
180
- export function linePointsFromCoordinates(input, pageHeight) {
194
+ export function linePointsFromCoordinates(input, transform) {
181
195
  const tuple = toNumericTuple(input, 4);
182
196
  if (!tuple) return null;
183
- const [x, y, x1, y1] = tuple;
184
- return [x, pageHeight - y, x1, pageHeight - y1];
197
+ const a = convertPdfPointToCanvas(transform, tuple[0], tuple[1]);
198
+ const b = convertPdfPointToCanvas(transform, tuple[2], tuple[3]);
199
+ return [a.x, a.y, b.x, b.y];
185
200
  }
186
201
  export function normalizedStrokeWidth(annotation, fallback = 1, options = {}) {
187
202
  const borderStyle = asBorderStyle(annotation.borderStyle);
@@ -44,6 +44,17 @@
44
44
  {{ state.selectedAnnotations.value.length }} selected
45
45
  </span>
46
46
 
47
+ <!-- Edit text (free text only) -->
48
+ <button
49
+ v-if="isFreeText"
50
+ class="flex items-center justify-center w-7 h-7 rounded-md hover:bg-default cursor-pointer"
51
+ title="Edit text"
52
+ data-testid="annotation-edit-text"
53
+ @click="onEditText"
54
+ >
55
+ <UIcon name="i-lucide-pencil" class="text-sm text-muted" />
56
+ </button>
57
+
47
58
  <!-- Delete -->
48
59
  <button
49
60
  class="flex items-center justify-center w-7 h-7 rounded-md hover:bg-default cursor-pointer"
@@ -114,6 +125,11 @@ function onFontSizeChange(value) {
114
125
  ann.fontSize = fontSize;
115
126
  state.selectedAnnotation.value = { ...ann };
116
127
  }
128
+ function onEditText() {
129
+ const ann = state.selectedAnnotation.value;
130
+ if (!ann || !state.painter.value) return;
131
+ void state.painter.value.editFreeText(ann.id);
132
+ }
117
133
  function onDelete() {
118
134
  if (isMultiSelect.value) {
119
135
  const ids = state.selectedAnnotations.value.map((a) => a.id);
@@ -1,15 +1,12 @@
1
+ import type { PageTransform } from '../annotation/pdf-import/transform.js';
1
2
  type __VLS_Props = {
2
3
  pageNumber: number;
3
4
  scale: number;
4
- pageWidth: number;
5
- pageHeight: number;
5
+ /** Carries displayed width/height plus the page's `/Rotate` and MediaBox
6
+ * origin — the single source of truth for mapping `/Rect` to canvas. */
7
+ pageTransform: PageTransform;
6
8
  pointerEvents: string;
7
9
  zIndex: number;
8
- /** PDF user-space origin offsets (`view[0]`, `view[1]`). Forwarded to
9
- * FormFieldWrapper so /Rect coordinates can be mapped to canvas space
10
- * when the page's MediaBox is not at (0, 0). */
11
- viewOffsetX?: number;
12
- viewOffsetY?: number;
13
10
  };
14
11
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
15
12
  declare const _default: typeof __VLS_export;
@@ -6,8 +6,8 @@
6
6
  position: 'absolute',
7
7
  top: 0,
8
8
  left: 0,
9
- width: props.pageWidth + 'px',
10
- height: props.pageHeight + 'px',
9
+ width: props.pageTransform.width + 'px',
10
+ height: props.pageTransform.height + 'px',
11
11
  transform: `scale(${props.scale})`,
12
12
  transformOrigin: '0 0',
13
13
  zIndex: props.zIndex,
@@ -22,10 +22,8 @@
22
22
  v-for="field in fields"
23
23
  :key="field.id"
24
24
  :field="field"
25
- :page-height="props.pageHeight"
25
+ :page-transform="props.pageTransform"
26
26
  :scale="props.scale"
27
- :view-offset-x="props.viewOffsetX ?? 0"
28
- :view-offset-y="props.viewOffsetY ?? 0"
29
27
  :selected="selectedFieldId === field.id"
30
28
  @select="formFields.selectPlacedField(field.id)"
31
29
  />
@@ -41,12 +39,9 @@ import FormFieldWrapper from "./form-fields/FormFieldWrapper.vue";
41
39
  const props = defineProps({
42
40
  pageNumber: { type: Number, required: true },
43
41
  scale: { type: Number, required: true },
44
- pageWidth: { type: Number, required: true },
45
- pageHeight: { type: Number, required: true },
42
+ pageTransform: { type: Object, required: true },
46
43
  pointerEvents: { type: String, required: true },
47
- zIndex: { type: Number, required: true },
48
- viewOffsetX: { type: Number, required: false },
49
- viewOffsetY: { type: Number, required: false }
44
+ zIndex: { type: Number, required: true }
50
45
  });
51
46
  const formFields = useFormFields();
52
47
  const state = useViewerState();
@@ -1,15 +1,12 @@
1
+ import type { PageTransform } from '../annotation/pdf-import/transform.js';
1
2
  type __VLS_Props = {
2
3
  pageNumber: number;
3
4
  scale: number;
4
- pageWidth: number;
5
- pageHeight: number;
5
+ /** Carries displayed width/height plus the page's `/Rotate` and MediaBox
6
+ * origin — the single source of truth for mapping `/Rect` to canvas. */
7
+ pageTransform: PageTransform;
6
8
  pointerEvents: string;
7
9
  zIndex: number;
8
- /** PDF user-space origin offsets (`view[0]`, `view[1]`). Forwarded to
9
- * FormFieldWrapper so /Rect coordinates can be mapped to canvas space
10
- * when the page's MediaBox is not at (0, 0). */
11
- viewOffsetX?: number;
12
- viewOffsetY?: number;
13
10
  };
14
11
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
15
12
  declare const _default: typeof __VLS_export;
@@ -29,10 +29,7 @@
29
29
  <FormFieldLayer
30
30
  :page-number="props.pageNumber"
31
31
  :scale="props.scale"
32
- :page-width="baseWidth"
33
- :page-height="baseHeight"
34
- :view-offset-x="viewOffsetX"
35
- :view-offset-y="viewOffsetY"
32
+ :page-transform="props.pageMeta"
36
33
  :pointer-events="formFieldPointerEvents"
37
34
  :z-index="formFieldZIndex"
38
35
  />
@@ -86,8 +83,6 @@ let currentRenderTask = null;
86
83
  let textLayer = null;
87
84
  const baseWidth = props.pageMeta.width;
88
85
  const baseHeight = props.pageMeta.height;
89
- const viewOffsetX = props.pageMeta.viewOffsetX ?? 0;
90
- const viewOffsetY = props.pageMeta.viewOffsetY ?? 0;
91
86
  const cssWidth = computed(() => baseWidth * props.scale);
92
87
  const cssHeight = computed(() => baseHeight * props.scale);
93
88
  const isAnnotating = computed(() => {
@@ -1,13 +1,12 @@
1
1
  import type { FormFieldDefinition } from '../../annotation/engine/types.js';
2
+ import { type PageTransform } from '../../annotation/pdf-import/transform.js';
2
3
  type __VLS_Props = {
3
4
  field: FormFieldDefinition;
4
- pageHeight: number;
5
+ /** Coord transform for this page. `/Rect` is in unrotated user space; the
6
+ * transform applies the page's `/Rotate` and the MediaBox origin so the
7
+ * field lands at the right canvas position regardless of page rotation. */
8
+ pageTransform: PageTransform;
5
9
  scale: number;
6
- /** PDF user-space origin (view[0], view[1]). /Rect coords are in
7
- * default user space; the canvas top-left corresponds to PDF
8
- * (viewOffsetX, viewOffsetY + pageHeight), so we have to subtract. */
9
- viewOffsetX?: number;
10
- viewOffsetY?: number;
11
10
  selected: boolean;
12
11
  };
13
12
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
@@ -26,7 +26,7 @@
26
26
  :field="props.field"
27
27
  :selected="props.selected"
28
28
  :scale="props.scale"
29
- :page-height="props.pageHeight"
29
+ :page-height="props.pageTransform.height"
30
30
  @select="emit('select')"
31
31
  />
32
32
  </div>
@@ -34,6 +34,7 @@
34
34
 
35
35
  <script setup>
36
36
  import { computed } from "vue";
37
+ import { convertPdfRectToCanvas } from "../../annotation/pdf-import/transform";
37
38
  import { useViewerState } from "../../composables/useViewerState";
38
39
  import { useFormFields } from "../../composables/useFormFields";
39
40
  import FormTextField from "./FormTextField.vue";
@@ -45,10 +46,8 @@ import FormButton from "./FormButton.vue";
45
46
  import PlacedFieldChrome from "./PlacedFieldChrome.vue";
46
47
  const props = defineProps({
47
48
  field: { type: Object, required: true },
48
- pageHeight: { type: Number, required: true },
49
+ pageTransform: { type: Object, required: true },
49
50
  scale: { type: Number, required: true },
50
- viewOffsetX: { type: Number, required: false },
51
- viewOffsetY: { type: Number, required: false },
52
51
  selected: { type: Boolean, required: true }
53
52
  });
54
53
  const emit = defineEmits(["select"]);
@@ -64,18 +63,13 @@ const chromeColor = computed(() => {
64
63
  });
65
64
  const positionStyle = computed(() => {
66
65
  const rect = props.field.rect;
67
- const vx = props.viewOffsetX ?? 0;
68
- const vy = props.viewOffsetY ?? 0;
69
- const left = rect[0] - vx;
70
- const top = props.pageHeight + vy - rect[3];
71
- const width = rect[2] - rect[0];
72
- const height = rect[3] - rect[1];
66
+ const r = convertPdfRectToCanvas(props.pageTransform, [rect[0], rect[1], rect[2], rect[3]]);
73
67
  return {
74
68
  position: "absolute",
75
- left: `${left}px`,
76
- top: `${top}px`,
77
- width: `${width}px`,
78
- height: `${height}px`
69
+ left: `${r.x}px`,
70
+ top: `${r.y}px`,
71
+ width: `${r.width}px`,
72
+ height: `${r.height}px`
79
73
  };
80
74
  });
81
75
  </script>
@@ -1,13 +1,12 @@
1
1
  import type { FormFieldDefinition } from '../../annotation/engine/types.js';
2
+ import { type PageTransform } from '../../annotation/pdf-import/transform.js';
2
3
  type __VLS_Props = {
3
4
  field: FormFieldDefinition;
4
- pageHeight: number;
5
+ /** Coord transform for this page. `/Rect` is in unrotated user space; the
6
+ * transform applies the page's `/Rotate` and the MediaBox origin so the
7
+ * field lands at the right canvas position regardless of page rotation. */
8
+ pageTransform: PageTransform;
5
9
  scale: number;
6
- /** PDF user-space origin (view[0], view[1]). /Rect coords are in
7
- * default user space; the canvas top-left corresponds to PDF
8
- * (viewOffsetX, viewOffsetY + pageHeight), so we have to subtract. */
9
- viewOffsetX?: number;
10
- viewOffsetY?: number;
11
10
  selected: boolean;
12
11
  };
13
12
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
@@ -2,8 +2,14 @@ import { type ShallowRef, type ComputedRef, type Ref } from 'vue';
2
2
  import type { PDFDocumentProxy } from 'pdfjs-dist';
3
3
  export interface PageMeta {
4
4
  pageNumber: number;
5
+ /** Displayed page width — already accounts for the page's intrinsic
6
+ * `/Rotate`, so width/height are swapped for 90°/270° pages. */
5
7
  width: number;
6
8
  height: number;
9
+ /** The page's intrinsic `/Rotate` value, normalized to 0 | 90 | 180 | 270.
10
+ * `getViewport()` bakes this in by default; annotation layers that map
11
+ * unrotated `/Rect` coordinates need it to apply the same rotation. */
12
+ rotation: number;
7
13
  /** PDF user-space origin of the page's visible area (`view[0]`, `view[1]`).
8
14
  * Almost always (0, 0); non-zero for PDFs whose MediaBox has been
9
15
  * shifted. Annotations in `/Rect` are in default user space, so any
@@ -124,10 +124,15 @@ export function createPageVirtualization() {
124
124
  const vy = view[1] ?? 0;
125
125
  const vw = view[2] ?? 0;
126
126
  const vh = view[3] ?? 0;
127
+ const w = vw - vx;
128
+ const h = vh - vy;
129
+ const rotation = (proxy.rotate % 360 + 360) % 360;
130
+ const swap = rotation % 180 !== 0;
127
131
  return {
128
132
  pageNumber,
129
- width: vw - vx,
130
- height: vh - vy,
133
+ width: swap ? h : w,
134
+ height: swap ? w : h,
135
+ rotation,
131
136
  viewOffsetX: vx,
132
137
  viewOffsetY: vy
133
138
  };
@@ -146,6 +151,7 @@ export function createPageVirtualization() {
146
151
  pageNumber: i,
147
152
  width: firstMeta.width,
148
153
  height: firstMeta.height,
154
+ rotation: firstMeta.rotation,
149
155
  viewOffsetX: firstMeta.viewOffsetX,
150
156
  viewOffsetY: firstMeta.viewOffsetY
151
157
  });
@@ -0,0 +1 @@
1
+ @source "./components/**/*.vue";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kviewer",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Kabema PDF Editor",
5
5
  "repository": "kabema/kviewer",
6
6
  "license": "MIT",
@@ -8,10 +8,12 @@
8
8
  "exports": {
9
9
  ".": {
10
10
  "types": "./dist/types.d.mts",
11
+ "style": "./dist/runtime/style.css",
11
12
  "import": "./dist/module.mjs"
12
13
  }
13
14
  },
14
15
  "main": "./dist/module.mjs",
16
+ "style": "./dist/runtime/style.css",
15
17
  "typesVersions": {
16
18
  "*": {
17
19
  ".": [
@@ -40,21 +42,21 @@
40
42
  "test:e2e:ui": "playwright test --ui"
41
43
  },
42
44
  "dependencies": {
45
+ "@iconify-json/lucide": "^1.2.101",
43
46
  "@nuxt/kit": "^4.4.2",
47
+ "@nuxt/ui": "^4.6.0",
44
48
  "konva": "^10.2.3",
45
49
  "pdf-lib": "^1.17.1",
46
50
  "pdfjs-dist": "^5.6.205",
47
51
  "tailwindcss": "^4.2.2"
48
52
  },
49
53
  "devDependencies": {
50
- "@iconify-json/lucide": "^1.2.101",
51
54
  "@nuxt/devtools": "^3.2.4",
52
55
  "@nuxt/eslint": "1.15.2",
53
56
  "@nuxt/eslint-config": "^1.15.2",
54
57
  "@nuxt/module-builder": "^1.0.2",
55
58
  "@nuxt/schema": "^4.4.2",
56
59
  "@nuxt/test-utils": "^4.0.0",
57
- "@nuxt/ui": "4.6.0",
58
60
  "@playwright/test": "^1.59.1",
59
61
  "@types/node": "latest",
60
62
  "canvas": "^3.2.3",