npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

neo-inkml

v0.1.5

Published

NeoStroke to W3C InkML converter

Readme

neo-inkml

Version npm License

NeoStroke to InkML converter

NeoStroke/IDot 기반 스트로크 데이터를 W3C InkML 표준 문서로 변환하는 TypeScript 라이브러리입니다.

✨ 주요 특징

🔄 스마트 데이터 변환

  • 표준 준수: W3C InkML 스펙 완전 준수
  • 메타데이터 보존: pageUuid, brushType, stroke ID 등
  • 출력 좌표 단위 제어: 기본 0.01pt(정수), px/pt/mm 등으로 확장 가능
  • 압력 처리 분리: 입력 해석(mode) ↔ 출력 스케일(range) 분리, 0-1 또는 0-4095 출력
  • 시간 채널 선택: ms(정수) 또는 s(소수)로 T 채널 출력, trace timestamp 보존
  • 브러시 안정화: 동일한 속성 조합을 정렬하여 브러시 정의/ID를 안정적으로 생성
  • 빈 그룹 제거: 유효 trace(2점 이상)가 없는 traceGroup은 출력하지 않음

🚀 설치

npm install neo-inkml

📋 기본 사용법

import { strokesToInkML } from "neo-inkml";

// NeoStroke 데이터
const strokes = [
  {
    key: "stroke-1",
    section: 3,
    owner: 27,
    book: 390,
    page: 69,
    brushType: 0,
    thickness: 0.2,
    color: "#000000",
    startTime: 1756274446921,
    dotArray: [
      { x: 10.92, y: 11.88, f: 0.696, deltaTime: 0 },
      { x: 10.98, y: 11.89, f: 0.729, deltaTime: 11 },
      // ...
    ],
  },
];

// InkML 변환
const inkml = strokesToInkML(strokes);

console.log(inkml);

📤 출력 결과

<ink xmlns="http://www.w3.org/2003/InkML">
  <definitions>
    <traceFormat xml:id="fmt_XYF">
      <channel name="X" type="integer" units="0.01pt"/>
      <channel name="Y" type="integer" units="0.01pt"/>
      <channel name="F" type="integer" units="dev" min="0" max="4095"/>
    </traceFormat>
    <brush xml:id="b1">
      <annotation type="color">#000000</annotation>
      <annotation type="tool">pen</annotation>
    </brush>
  </definitions>
  <traceGroup xml:id="g_3_27_390_69">
    <trace contextRef="fmt_XYF" timeOffset="1756274446921" xml:id="stroke-1" brushRef="#b1">
      1092 1188 2846, 1098 1189 2981
    </trace>
  </traceGroup>
</ink>

⚙️ InkmlOptions 요약

interface InkmlOptions {
  // 좌표 단위
  inputUnit?: 'device' | 'px' | 'pt' | 'mm' | '0.01pt'; // 기본 'device'
  outputUnit?: 'device' | 'px' | 'pt' | 'mm' | '0.01pt'; // 기본 '0.01pt'
  dpi?: number;             // px 변환용, 기본 72
  outputPrecision?: number; // 소수 단위 출력 자리수, 기본 2
  // 시간
  timeUnit?: 'ms' | 's';      // 기본 'ms' (s일 때 소수)
  includeDeltaTime?: boolean; // 기본 true (T 채널 포함 여부)
  includeTimestamp?: boolean; // 기본 true (trace 속성)
  // 압력
  pressure?: {
    mode: 'auto' | 'raw' | 'normalized'; // 입력 해석, 기본 'auto'
    range?: 'auto' | '0-1' | '0-4095';   // 출력 스케일, 기본 'auto'(=0-4095)
    max?: number;                        // raw 분모(없으면 stroke.maxPenPressureValue→4095)
  };
  // 기타
  applyAffine?: boolean;   // 좌표 변환 전 affine 적용, 기본 false
  channels?: { force?: boolean, tilt?: boolean }; // force 채널 포함, 기본 true / TiltX/TiltY 채널 포함, 기본 false
  grouping?: 'page' | 'document'; // 기본 'page'
  includeBrush?: boolean;  // brush 정의/참조 포함, 기본 true
  metadata?: { producer?: string; version?: string };
}

좌표 단위 예시

  • input=device, output=0.01pt(기본): device를 0.01pt 정수로 변환
  • input=px, output=mm, dpi=96: 픽셀을 밀리미터로 변환(소수 2자리)

압력 스케일 예시

  • mode=auto, range=0-4095: f=0.7 → 2867, f=820 → 820
  • mode=raw, range=0-1, max=1023: f=460 → 0.45

시간 채널 예시

  • includeDeltaTime=true, timeUnit='ms': T=정수(ms)
  • includeDeltaTime=true, timeUnit='s' : T=초(소수 3자리)

동작 노트

  • 빈 traceGroup 제거: 그룹 내 유효 trace(2점 이상)가 없으면 출력하지 않습니다.
  • 브러시 안정화: 동일 속성 조합을 정렬하여 ID를 재부여하고, stroke의 brushRef를 재계산합니다.

🗂️ 데이터 형식

StrokeData 인터페이스

interface StrokeData {
  key: string; // 스트로크 고유 ID
  section: number; // 섹션 번호
  owner: number; // 소유자 번호
  book: number; // 책 번호
  page: number; // 페이지 번호
  brushType: number; // 브러시 타입 (0=pen, 1=marker...)
  thickness: number; // 선 두께
  color: string; // 색상 (#rrggbb)
  startTime: number; // 시작 시간 (ms)
  endTime?: number; // 종료 시간 (ms)
  dotArray: DotData[]; // 점 데이터 배열
  pageUuid?: string; // 페이지 UUID
  affineMatrix?: number[]; // 변환 행렬
}

DotData 인터페이스

interface DotData {
  x: number; // X 좌표
  y: number; // Y 좌표
  f: number; // 압력값
  deltaTime: number; // 이전 점으로부터 시간 간격 (ms)
  time?: number; // 절대 시간 (ms)
  dotType: number; // 점 타입 (1=down, 2=move, 3=up)
}

🎨 브러시 타입 매핑

| 숫자 | 문자열 | 설명 | | ---- | ----------- | -------------------- | | 0 | pen | 일반 펜 | | 1 | eraser | 지우개 | | 2 | marker | 마커 | | 3 | pencil | 마우스 펜 |

📝 개발 및 빌드

# 의존성 설치
npm install

# TypeScript 컴파일
npm run build

# 변환 테스트
npm test

🏗️ 프로젝트 구조

src/
├── index.ts              # 메인 API
├── options.ts            # 옵션 정의
├── types/
│   └── neo-types.ts      # 타입 정의
└── core/
    ├── serializer.ts     # InkML 직렬화
    ├── mappers.ts        # 데이터 변환
    ├── trace-format.ts   # TraceFormat 생성
    └── brush.ts          # 브러시 처리

배경 이미지 임베딩

  • 그룹 키 정책: section.owner.book.page 점(.) 구분자 사용. 예: 3.27.390.69
  • options.backgrounds[groupKey]에 base64 이미지를 지정하면 해당 traceGroup 헤더에 annotation으로 삽입
  • sx/sy 생략 시 자동 스케일: outputUnit과 dpi로 계산(기본 0.01pt일 때 sx=sy=72*100/dpi)

예시(사용 코드)

import { strokesToInkML } from 'neo-inkml';

const inkml = strokesToInkML(strokes, {
  outputUnit: '0.01pt',
  dpi: 300,
  backgrounds: {
    '3.27.390.69': {
      source: { type: 'data', mime: 'image/png', data: 'iVBORw0K...base64...' },
      size: { width_px: 2480, height_px: 3508, dpi: 300 },
      place: { x: 0, y: 0, opacity: 1 }
    }
  }
} as any);

예시(생성되는 annotation)

<annotation type="backgroundImage" mime="image/png" encoding="base64"
            width_px="2480" height_px="3508" dpi="300"
            x="0" y="0" sx="24" sy="24" opacity="1">iVBORw0K...</annotation>