You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Android平台用Java打开Skim笔记文件(.skim)

Android导入Skim(.skim)笔记文件的实现思路与示例代码

一、先明确Skim文件的本质

Skim的.skim文件是XML格式的Apple Property List(plist),内部存储了PDF关联的所有笔记数据,包括高亮、批注、下划线、页码、位置坐标、笔记内容等核心信息。实现导入的核心是解析该plist文件,再将数据映射到你现有App的笔记系统中。

二、核心实现步骤与代码示例

1. 读取并解析XML plist

Android原生可通过XmlPullParser解析XML格式的plist,也可使用第三方plist解析库简化操作。以下是原生解析的核心代码片段:

// 假设已获取.skim文件的输入流
InputStream inputStream = new FileInputStream(skimFile);
XmlPullParser parser = Xml.newPullParser();
parser.setInput(inputStream, "UTF-8");

int eventType = parser.getEventType();
boolean inNotesArray = false;
boolean inNoteItem = false;
HashMap<String, String> currentNote = null;
List<HashMap<String, String>> skimNotes = new ArrayList<>();

while (eventType != XmlPullParser.END_DOCUMENT) {
    String tagName = parser.getName();
    switch (eventType) {
        case XmlPullParser.START_TAG:
            if ("array".equals(tagName)) {
                String key = parser.getAttributeValue(null, "key");
                if ("Notes".equals(key)) {
                    inNotesArray = true;
                }
            } else if ("dict".equals(tagName) && inNotesArray) {
                inNoteItem = true;
                currentNote = new HashMap<>();
            } else if ("key".equals(tagName) && inNoteItem) {
                String key = parser.nextText();
                eventType = parser.next();
                if (eventType == XmlPullParser.TEXT) {
                    currentNote.put(key, parser.getText());
                }
            }
            break;
        case XmlPullParser.END_TAG:
            if ("dict".equals(tagName) && inNoteItem) {
                inNoteItem = false;
                skimNotes.add(currentNote);
            } else if ("array".equals(tagName) && inNotesArray) {
                inNotesArray = false;
            }
            break;
    }
    eventType = parser.next();
}
inputStream.close();

2. 解析Skim笔记的关键字段

每个Skim笔记条目(dict节点)的核心字段:

  • Type:笔记类型,如Highlight(高亮)、Note(批注)、Underline(下划线)
  • Page:关联的PDF页码(从1开始,与多数PDF渲染库规则一致)
  • Content:批注文本内容(仅Note类型包含)
  • Rect:笔记在PDF页面的位置,格式为x,y,width,height(PDF坐标原点在左下角)
  • Color:笔记颜色,格式为RGB值(如1.0,0.0,0.0代表红色)

3. 映射到现有笔记系统

将解析出的Skim数据转换为你App的笔记模型,示例如下:

// 假设你已有NoteModel类,包含type、page、content、rect、color等字段
List<NoteModel> appNotes = new ArrayList<>();
for (HashMap<String, String> skimNote : skimNotes) {
    NoteModel note = new NoteModel();
    // 映射笔记类型
    String skimType = skimNote.get("Type");
    if ("Highlight".equals(skimType)) {
        note.setType(NoteModel.TYPE_HIGHLIGHT);
    } else if ("Note".equals(skimType)) {
        note.setType(NoteModel.TYPE_NOTE);
        note.setContent(skimNote.get("Content"));
    } else if ("Underline".equals(skimType)) {
        note.setType(NoteModel.TYPE_UNDERLINE);
    }
    // 映射页码
    note.setPage(Integer.parseInt(skimNote.get("Page")));
    // 映射位置Rect(需注意坐标转换)
    String rectStr = skimNote.get("Rect");
    String[] rectParts = rectStr.split(",");
    float x = Float.parseFloat(rectParts[0]);
    float y = Float.parseFloat(rectParts[1]);
    float width = Float.parseFloat(rectParts[2]);
    float height = Float.parseFloat(rectParts[3]);
    // PDF原点在左下角,若你的视图原点在左上角,需转换y坐标:y = pageHeight - y - height
    note.setRect(new RectF(x, y, x + width, y + height));
    // 映射颜色
    String colorStr = skimNote.get("Color");
    String[] colorParts = colorStr.split(",");
    float r = Float.parseFloat(colorParts[0]);
    float g = Float.parseFloat(colorParts[1]);
    float b = Float.parseFloat(colorParts[2]);
    note.setColor(Color.rgb((int)(r*255), (int)(g*255), (int)(b*255)));
    
    appNotes.add(note);
}
// 将转换后的笔记存入现有存储
yourNoteManager.addNotes(appNotes);

三、注意事项

  • 部分.skim文件为二进制plist格式,需先转换为XML格式再解析,可使用Android端二进制plist解析库处理。
  • 坐标转换:PDF坐标原点在左下角,Android视图原点通常在左上角,需根据PDF页面高度调整y坐标。
  • 扩展支持:Skim还支持StrikeThrough(删除线)等类型,可按需扩展映射逻辑。

内容的提问来源于stack exchange,提问作者Gin371

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 01:42:35