mokeyish / obsidian-enhancing-export

This is an enhancing export plugin base on Pandoc for Obsidian (https://obsidian.md/ ). It's allow you to export to formats like Markdown、Markdown (Hugo https://gohugo.io/ )、Html、docx、Latex etc.
MIT License
342 stars 24 forks source link

Make plugin compatible with wikilinks and all paths #74

Closed Comprehensive-Jason closed 9 months ago

Comprehensive-Jason commented 1 year ago

Currently, the plugin is not compatible with wikilinks and only works on embeds with relative file paths. The Obsidian Link Converter plugin can convert all the wikilinks in a file into Markdown, and all paths to relative file paths. The Converter plugin's code could be integrated into this plugin to increase Obsidian compatibility. Wikilinks and paths could be automatically converted into markdown and relative paths.

If needed, I can help with this project. Could have a couple of pointers on where best to add the necessary code in this repo?

mokeyish commented 1 year ago

Thanks. There are two ways to support wikilinks.

What do your think?

Comprehensive-Jason commented 1 year ago

I see. I'm way more familiar with the Typescript approach, as I help code another plugin.

I might hold off on working on it for now, as exporting to standard markdown is on the Obsidian roadmap. Once that comes out, it will probably be the best solution.


In the meantime, if anyone else is looking for ways to convert their notes for export, they can use the Link Converter plugin mentioned above to prepare their files for export. I personally use the Obsidian Commander plugin to create the following macro for exporting: 200

I duplicate the note to be exported (so my export preparation doesn't affect the actual note), then use Link Converter to turn all links into standard markdown, then run Linter to standardize my note to match standard Markdown specs and avoid formatting issues with Pandoc, then export. I hope this is helpful!

FeralFlora commented 1 year ago

@Comprehensive-Jason That's comprehensive alright! Thanks for the Commander macro suggestion!

Comprehensive-Jason commented 1 year ago

I talked with the Obsidian devs about whether exporting to standard markdown is still a planned feature. They told me the feature will come later and to go ahead with the workaround. I will start work on adding the conversion code.

vanem commented 1 year ago

In any case, this plugin uses Pandoc, and it is not limited to conversion to Markdown, so it will be relevant even if the Obsidian devs implement .md export

Comprehensive-Jason commented 1 year ago

@vanem I'm not trying to convert just to standard Markdown, I'm trying to preprocess Obsidian notes into standard Markdown so that this plugin works correctly.

The chief issue with this plugin right now is that it (and Pandoc) requires standard Markdown links with relative file paths in order to convert properly, while most users of Obsidian use wikilinks with just the file name.

Extra code is needed to convert Obsidian wikilinks into standard Markdown so that the plugin works for everyday users.

FeralFlora commented 1 year ago

This plugin has been compatible with wikilinks for a while using Pandoc extensions, and yesterday, it also became compatible with wikilink images with the latest release of Pandoc! 🎉

The specific attribute that you need, to enable wikilink support, is:

wikilinks_title_after_pipe

To use it, change the default export command (for example to PDF) to:

-f markdown+wikilinks_title_after_pipe --resource-path="${currentPath}" --resource-path="${attachmentFolderPath}" --metadata title="${currentFileName}" -s -o "${outputPath}" -t pdf

You can also add other extensions like mark and lists_without_preceding_blankline that improve Obsidian and Pandoc compatibility, so it becomes:

-f markdown+wikilinks_title_after_pipe+mark+lists_without_preceding_blankline --resource-path="${currentPath}" --resource-path="${attachmentFolderPath}" --metadata title="${currentFileName}" -s -o "${outputPath}" -t pdf

Also note that I removed some bloat in the command, as mentioned in #115.

Comprehensive-Jason commented 1 year ago

This is pretty cool! I presume we still have to convert to relative file paths though?

FeralFlora commented 1 year ago

I presume we still have to convert to relative file paths though?

Nope, not as long as you supply the resource-path, as in my example above (which is included by default, I think), then it should work with only the filename. Upgrade to Pandoc 3.1.7 and give it a try!

However, note that image sizing is not working yet, since you can't use link attributes with wikilinks. I've raised this issue on the Pandoc repo here, but the devs are reluctant to support the combination of Wikilinks + link attributes, since the apps that use wikilinks don't support the link attributes syntax. You're welcome to join the conversation over on the Pandoc repo!

On the Obsidian side, there's discussion in Forum about supporting link attributes or expanding the existing image sizing syntax to support units here.

Comprehensive-Jason commented 1 year ago

@FeralFlora Gotcha. Currently I'm testing with the following:

Arguments: -f markdown+wikilinks_title_after_pipe+mark+lists_without_preceding_blankline --resource-path="${currentDir}" --resource-path="${attachmentFolderPath}" --lua-filter="${luaDir}/pdf.lua" ${ options.textemplate ? `--resource-path="${pluginDir}/textemplate" --template="${options.textemplate}"` : ` ` } --embed-resources --standalone -s -o "${outputPath}" -t pdf
Extra arguments: --pdf-engine=pdflatex

Internal links and embedding works fine with the following folder structures:

Vault
└── Attachments Folder (set in Obsidian)
    └── imageToEmbed.png
    └── note.md

OR

Vault
└── note.md
└── Attachments Folder (set in Obsidian)
    └── imageToEmbed

When ![[imageToEmbed.png]] is written in note.md, and note.md is placed inside the Attachments folder alongside the image such that they share the exact same directory, the embedding works properly. Embedding also works properly if note.md is kept outside of any folders while inside the vault.


However, when I use a more normal folder structure, where note.md is in its own folder separate from the image:

Vault
└── Attachments Folder (set in Obsidian)
    └── imageToEmbed.png
└── Notes Folder
    └──note.md

I get the following error: image

I think the issue is with how the plugin is replacing ${attachmentFolderPath}. The plugin replaces "${currentDir}" with "C:\Users\jason\AppData\Roaming\obsidian\Obsidian Sandbox\Notes". However, it only replaces "${attachmentFolderPath}" with "Attachments", omitting the rest of the path.

When I explicitly insert the full path "C:\Users\jason\AppData\Roaming\obsidian\Obsidian Sandbox\Attachments" instead of using ${attachmentFolderPath}, everything exports perfectly.

All in all, I think this is quite a minor fix for the plugin. First, we replace ${attachmentFolderPath} with ${vaultDir}/${attachmentFolderPath} in all the templates. Then, we could add a toggle in the plugin settings that appends +wikilinks_title_after_pipe so the user can quickly enable wikilink compatibility.

mrlinuxfish commented 1 year ago

-f markdown+wikilinks_title_after_pipe --resource-path="${currentPath}" --resource-path="${attachmentFolderPath}" --metadata title="${currentFileName}" -s -o "${outputPath}" -t pdf

For some reason, using these settings I can't export contents of an expanded link such as ![[note]] because it thinks the links to notes are images. Using the default pdf export command had these links showing as plain text which is also not what I was aiming for.

Comprehensive-Jason commented 1 year ago

@mrlinuxfish This is a different issue tracked in #14 .

FeralFlora commented 1 year ago

However, when I use a more normal folder structure, where note.md is in its own folder separate from the image:

For me, I can export with the command I shared, even though my files are located as follows:

Vault
└── Inbox
    └──note.md
 └── Attachments Folder (set in Obsidian)
    └── imageToEmbed.png

I just realized that this is because I have the full resource path in my defaults file. This is what enhancing export prints in the console: pandoc -f markdown+wikilinks_title_after_pipe+mark+lists_without_preceding_blankline --resource-path="C:\Users\User\Documents\Notes\Obsidian-notes\01 - Inbox\Cat test.md" --resource-path="06 - Resources" --metadata title="Cat test" -s -o "C:\Users\User\Documents\Notes\Obsidian-notes\09 - Outputs/Cat test.pdf" -t pdf --defaults=C:\Users\user\Documents\Notes\Obsidian-notes\pandoc.yaml --citeproc "C:\Users\User\Documents\Notes\Obsidian-notes\01 - Inbox\Cat test.md"

So I guess it would fail for me without the defaults file too, and you are right that ${vaultDir} is needed in the template. Perhaps a better solution would be for ${attachmentFolderPath}} to actually resolve as the full path to the attachments folder.

Then, we could add a toggle in the plugin settings that appends +wikilinks_title_after_pipe so the user can quickly enable wikilink compatibility.

I agree, such a toggle would be nice.

Comprehensive-Jason commented 1 year ago

@FeralFlora Sounds like a plan! I'll get working on a PR for it then.

TimFruit commented 1 year ago

Thanks. There are two ways to support wikilinks.

* Convert the markdown with TypeScript(like [Obsidian Link Converter plugin](https://github.com/ozntel/obsidian-link-converter)). and fill stdin of pandoc.

* Write a [lua-filter](https://pandoc.org/lua-filters.html) named `wiki-links.lua` to do convertion, and put it into [luaDir](https://github.com/mokeyish/obsidian-enhancing-export/tree/main/lua).
  Then update the export template command (add `--lua-filter="${luaDir}/wiki-links.lua"` before other lua-filter)
  https://github.com/mokeyish/obsidian-enhancing-export/blob/04176511ba5f07bfa1073919bee0c12a9ceb4855/src/export_command_templates.ts#L25

  to
  ```diff
  - -f markdown --resource-path="${currentDir}" --resource-path="${attachmentFolderPath}" --lua-filter="${luaDir}/markdown.lua" -s -o "${outputPath}" -t commonmark_x-attributes
  + -f markdown --resource-path="${currentDir}" --resource-path="${attachmentFolderPath}" --lua-filter="${luaDir}/wiki-links.lua"  --lua-filter="${luaDir}/markdown.lua" -s -o "${outputPath}" -t commonmark_x-attributes
  ```

  Visit https://pandoc.org/try/
  <img alt="图片" width="1403" src="https://user-images.githubusercontent.com/16131917/240490128-5637d4f3-4e01-4017-a37c-a5ce03f21a79.png">

What do your think?

Thanks, i use the second way,it works

here is the lua code:

function Pandoc(doc)
  -- 遍历文档中的所有块元素
  for i, block in pairs(doc.blocks) do
    if block.t == 'Para' then
      -- 在段落文本中进行替换
      doc.blocks[i] = pandoc.Para(replace_wikilinks(block.content))
    end
  end
  return doc
end

function replace_wikilinks(content)
  local new_content = {}
  local text = pandoc.utils.stringify(content)

  -- 使用正则表达式匹配文本中的 Wikilink 格式
  text = text:gsub("!(%[%[[^%]]*%]%])", function(link)
    local img_path = link:match("%[%[([^%]]*)%]%]")
    return "![](" .. img_path .. ")"
  end)

  return pandoc.read(text).blocks[1].content
end

return { { Pandoc = Pandoc } }
mokeyish commented 1 year ago

@TimFruit Looks great, Are you willing to submit a PR?

TimFruit commented 1 year ago

@TimFruit Looks great, Are you willing to submit a PR?

sorry,i don't want to submit a PR. Writing pandoc lua scripts is difficult for me, I asked chat gpt to get this code

FeralFlora commented 1 year ago

@TimFruit Looks great, Are you willing to submit a PR?

For standard markdown, wouldn't it be easier to just add the wikilinks_title_after_pipe extension (Pandoc docs here) rather than using a lua filter to convert the wikilinks to markdown links?

As is, gfm and commonmark don't support the use wikilinks_title_after_pipe extension for image links yet, but it looks like support will be added for that soon. Progress on that is tracked here: https://github.com/jgm/pandoc/issues/9164

Until then, the lua filter might be relevant for gfm and commonmark, but I think the most straightforward option for markdown is to just use the extension.

@Comprehensive-Jason mentioned making a PR to change the default command, not sure if there's any progress on that? This requires ${attachmentFolderPath}} to actually resolve as the full path to the attachments folder. Alternatively, the commands could be changed from ${attachmentFolderPath} to ${vaultDir}/${attachmentFolderPath}.

universvm commented 11 months ago

@Comprehensive-Jason I was wondering whether you need help with the PR?

daeh commented 9 months ago

Just voicing my enthusiastic support for this

mokeyish commented 9 months ago

154

jeetsukumaran commented 8 months ago

Folks, I've been working on a similar problem, to get Obsidian style "|100" to set the width for images in pandoc beamer output.

Thought I would share some code here in case it helps with this problem.

The following works for wikilink images to propagate any given width/height attributes to resize the image:

function Image(elem)
    local captionStr = pandoc.utils.stringify(elem.caption)
    local parts = {}
    for part in string.gmatch(captionStr, "[^|]+") do
        table.insert(parts, string.match(part, "^%s*(.-)%s*$")) -- Trim whitespace
    end
    local width, height = nil, nil
    if #parts > 0 then
        width, height = parts[#parts]:match("^(%d+)%s*x?%s*(%d*)$")
        if width then
            table.remove(parts, #parts)
        end
    end
    if width then
        elem.attributes.width = width
        if height and height ~= "" then
            elem.attributes.height = height
        end
    end
    if #parts > 0 then
        local newCaption = table.concat(parts, " | ")
        elem.caption = pandoc.read(newCaption, 'markdown').blocks[1].content
    else
        elem.caption = {}
    end
    return elem
end

e.g.

![[attachments/files/Pasted image 20240222051816.png| 100]]

![[attachments/files/Pasted image 20240222051816.png| Real caption. | 100]]

P.s.

The approach is not perfect for my use case because I would like to remove the width attribute from the display figure caption. Problem is that even thought the element caption is updated, the figure remains showing the old caption.

As a MWE to illustrate the problem, try this filter:

function Image(elem)
    elem.caption = pandoc.List({pandoc.Str("foo")});
    return elem
end

return {
    {
        Image = Image,
    }
}

when applied to a document with:

![[attachments/files/Pasted image 20240222051816.png| real caption | 100]]

we get:

image

(image hidden; only caption shown)

So the image caption is updated, but the figure text is not.


It seems like pandoc generates the figure text before the lua filter sees element. The following for example updates no "Figure" captions, though the element is updated.

function Image(elem)
    elem.caption = pandoc.List({pandoc.Str("foo")});
    return elem
end

return {
    {
        Image = Image,
    }
}
FeralFlora commented 6 months ago

Perhaps a better solution would be for ${attachmentFolderPath}} to actually resolve as the full path to the attachments folder.

@Comprehensive-Jason @universvm I just submitted a PR which introduces this change: https://github.com/mokeyish/obsidian-enhancing-export/pull/174