Fix support hyperlink reader (#508)

* fix

* fix: support external link

* wip

* fix

Co-authored-by: bokuweb <bokuweb@bokuwebnombp.lan>
main
bokuweb 2022-07-06 10:47:15 +09:00 committed by GitHub
parent 674499a436
commit a346cd1a16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
69 changed files with 2214 additions and 676 deletions

View File

@ -4,17 +4,16 @@ pub fn main() -> Result<(), DocxError> {
let path = std::path::Path::new("./output/hyperlink.docx");
let file = std::fs::File::create(&path).unwrap();
Docx::new()
.add_paragraph(
Paragraph::new().add_hyperlink(
Hyperlink::new()
.anchor("anchor")
.add_run(Run::new().add_text("Hello")),
),
)
.add_paragraph(Paragraph::new().add_hyperlink(
Hyperlink::new("anchor", HyperlinkType::Anchor).add_run(Run::new().add_text("Hello")),
))
.add_bookmark_start(1, "anchor")
.add_paragraph(
Paragraph::new()
.add_run(Run::new().add_text("World"))
.add_hyperlink(
Hyperlink::new("https://google.com", HyperlinkType::External)
.add_run(Run::new().add_text(" World")),
)
.page_break_before(true),
)
.add_bookmark_end(1)

View File

@ -10,6 +10,7 @@ pub struct DocumentRels {
pub has_comments: bool,
pub has_numberings: bool,
pub images: Vec<(String, String)>,
pub hyperlinks: Vec<(String, String)>,
pub custom_xml_count: usize,
pub header_count: usize,
pub footer_count: usize,
@ -29,6 +30,11 @@ impl DocumentRels {
self.images.push((id.into(), path.into()));
self
}
pub fn add_hyperlinks(mut self, id: impl Into<String>, path: impl Into<String>) -> Self {
self.hyperlinks.push((id.into(), path.into()));
self
}
}
impl BuildXML for DocumentRels {
@ -106,6 +112,15 @@ impl BuildXML for DocumentRels {
)
}
for (id, path) in self.hyperlinks.iter() {
b = b.relationship_with_mode(
id,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
path,
"External",
)
}
b.close().build()
}
}

View File

@ -2,34 +2,51 @@ use serde::Serialize;
use super::*;
use crate::documents::BuildXML;
use crate::xml_builder::*;
use crate::types::*;
use crate::{create_hyperlink_rid, generate_hyperlink_id, xml_builder::*};
#[derive(Serialize, Debug, Clone, PartialEq, Default)]
#[derive(Serialize, Debug, Clone, PartialEq)]
#[serde(tag = "type")]
#[serde(rename_all = "camelCase")]
pub enum HyperlinkData {
External {
rid: String,
// path is writer only
#[serde(skip_serializing_if = "String::is_empty")]
path: String,
},
Anchor {
anchor: String,
},
}
#[derive(Serialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Hyperlink {
pub rid: Option<String>,
pub anchor: Option<String>,
pub history: bool,
#[serde(flatten)]
pub link: HyperlinkData,
pub history: Option<usize>,
pub children: Vec<ParagraphChild>,
}
impl Hyperlink {
pub fn new() -> Self {
Hyperlink::default()
pub fn new(value: impl Into<String>, t: HyperlinkType) -> Self {
let link = {
match t {
HyperlinkType::External => HyperlinkData::External {
rid: create_hyperlink_rid(generate_hyperlink_id()),
path: value.into(),
},
HyperlinkType::Anchor => HyperlinkData::Anchor {
anchor: value.into(),
},
}
pub fn rid(mut self, rid: impl Into<String>) -> Self {
self.rid = Some(rid.into());
self
};
Hyperlink {
link,
history: None,
children: vec![],
}
pub fn anchor(mut self, anchor: impl Into<String>) -> Self {
self.anchor = Some(anchor.into());
self
}
pub fn history(mut self) -> Self {
self.history = true;
self
}
pub fn add_run(mut self, run: Run) -> Self {
@ -81,11 +98,24 @@ impl Hyperlink {
impl BuildXML for Hyperlink {
fn build(&self) -> Vec<u8> {
let b = XMLBuilder::new();
b.open_hyperlink(self.rid.as_ref(), self.anchor.as_ref(), self.history)
.add_children(&self.children)
.close()
.build()
let mut b = XMLBuilder::new();
match self.link {
HyperlinkData::Anchor { ref anchor } => {
b = b.open_hyperlink(
None,
Some(anchor.clone()).as_ref(),
Some(self.history.unwrap_or(1)),
)
}
HyperlinkData::External { ref rid, .. } => {
b = b.open_hyperlink(
Some(rid.clone()).as_ref(),
None,
Some(self.history.unwrap_or(1)),
)
}
};
b.add_children(&self.children).close().build()
}
}
@ -99,13 +129,11 @@ mod tests {
#[test]
fn test_hyperlink() {
let l = Hyperlink::new()
.anchor("ToC1")
.add_run(Run::new().add_text("hello"));
let l = Hyperlink::new("ToC1", HyperlinkType::Anchor).add_run(Run::new().add_text("hello"));
let b = l.build();
assert_eq!(
str::from_utf8(&b).unwrap(),
r#"<w:hyperlink w:anchor="ToC1"><w:r><w:rPr /><w:t xml:space="preserve">hello</w:t></w:r></w:hyperlink>"#
r#"<w:hyperlink w:anchor="ToC1" w:history="1"><w:r><w:rPr /><w:t xml:space="preserve">hello</w:t></w:r></w:hyperlink>"#
);
}
}

View File

@ -85,8 +85,7 @@ impl BuildXML for Vec<TableOfContentsItem> {
if t.instr.hyperlink {
p = p.add_hyperlink(
Hyperlink::new()
.anchor(&t.toc_key)
Hyperlink::new(&t.toc_key, crate::types::HyperlinkType::Anchor)
.add_run(run)
.add_run(Run::new().add_tab())
.add_run(page_ref),
@ -120,8 +119,7 @@ impl BuildXML for Vec<TableOfContentsItem> {
if t.instr.hyperlink {
p = p.add_hyperlink(
Hyperlink::new()
.anchor(&t.toc_key)
Hyperlink::new(&t.toc_key, HyperlinkType::Anchor)
.add_run(run)
.add_run(Run::new().add_tab())
.add_run(page_ref),

View File

@ -0,0 +1,22 @@
#[cfg(not(test))]
use std::sync::atomic::AtomicUsize;
#[cfg(not(test))]
static HYPERLINK_ID: AtomicUsize = AtomicUsize::new(1);
#[cfg(not(test))]
pub fn generate_hyperlink_id() -> usize {
use std::sync::atomic::Ordering;
let id = HYPERLINK_ID.load(Ordering::Relaxed);
HYPERLINK_ID.store(id.wrapping_add(1), Ordering::Relaxed);
id
}
#[cfg(test)]
pub fn generate_hyperlink_id() -> usize {
123
}
pub fn create_hyperlink_rid(id: usize) -> String {
format!("rIdHyperlink{}", id)
}

View File

@ -18,6 +18,7 @@ mod footer_id;
mod header;
mod header_id;
mod history_id;
mod hyperlink_id;
mod numberings;
mod paragraph_id;
mod paragraph_property_change_id;
@ -36,6 +37,7 @@ mod xml_docx;
pub(crate) use build_xml::BuildXML;
pub(crate) use history_id::HistoryId;
pub(crate) use hyperlink_id::*;
use image::ImageFormat;
pub(crate) use paragraph_id::*;
pub(crate) use paragraph_property_change_id::ParagraphPropertyChangeId;
@ -478,7 +480,7 @@ impl Docx {
pub fn build(mut self) -> XMLDocx {
self.reset();
self.update_comments();
self.update_dependencies();
let tocs: Vec<(usize, Box<TableOfContents>)> = self
.document
@ -572,7 +574,7 @@ impl Docx {
pub fn json_with_update_comments(&mut self) -> String {
self.reset();
self.update_comments();
self.update_dependencies();
serde_json::to_string_pretty(&self).unwrap()
}
@ -597,11 +599,13 @@ impl Docx {
}
// Traverse and clone comments from document and add to comments node.
fn update_comments(&mut self) {
fn update_dependencies(&mut self) {
let mut comments: Vec<Comment> = vec![];
let mut comments_extended: Vec<CommentExtended> = vec![];
let mut comment_map: HashMap<usize, String> = HashMap::new();
let mut hyperlink_map: HashMap<String, String> = HashMap::new();
for child in &self.document.children {
match child {
DocumentChild::Paragraph(paragraph) => {
@ -610,6 +614,9 @@ impl Docx {
self.insert_comment_to_map(&mut comment_map, c);
}
if let ParagraphChild::Hyperlink(h) = child {
if let HyperlinkData::External { rid, path } = h.link.clone() {
hyperlink_map.insert(rid, path);
};
for child in &h.children {
if let ParagraphChild::CommentStart(c) = child {
self.insert_comment_to_map(&mut comment_map, c);
@ -619,11 +626,12 @@ impl Docx {
}
}
DocumentChild::Table(table) => {
collect_comments_in_table(
collect_dependencies_in_table(
table,
&mut comments,
&mut comments_extended,
&mut comment_map,
&mut hyperlink_map,
);
}
_ => {}
@ -643,6 +651,9 @@ impl Docx {
);
}
if let ParagraphChild::Hyperlink(h) = child {
if let HyperlinkData::External { rid, path } = h.link.clone() {
hyperlink_map.insert(rid, path);
};
for child in &h.children {
if let ParagraphChild::CommentStart(c) = child {
push_comment_and_comment_extended(
@ -657,11 +668,12 @@ impl Docx {
}
}
DocumentChild::Table(table) => {
collect_comments_in_table(
collect_dependencies_in_table(
table,
&mut comments,
&mut comments_extended,
&mut comment_map,
&mut hyperlink_map,
);
}
_ => {}
@ -677,6 +689,10 @@ impl Docx {
.add_comments_extended(comments_extended);
self.comments.add_comments(comments);
for (id, d) in hyperlink_map {
self.document_rels.hyperlinks.push((id, d));
}
}
// Traverse and clone comments from document and add to comments node.
@ -772,11 +788,12 @@ impl Docx {
}
}
fn collect_comments_in_table(
fn collect_dependencies_in_table(
table: &Table,
comments: &mut Vec<Comment>,
comments_extended: &mut Vec<CommentExtended>,
comment_map: &mut HashMap<usize, String>,
hyperlink_map: &mut HashMap<String, String>,
) {
for TableChild::TableRow(row) in &table.rows {
for TableRowChild::TableCell(cell) in &row.cells {
@ -793,6 +810,9 @@ fn collect_comments_in_table(
);
}
if let ParagraphChild::Hyperlink(h) = child {
if let HyperlinkData::External { rid, path } = h.link.clone() {
hyperlink_map.insert(rid, path);
};
for child in &h.children {
if let ParagraphChild::CommentStart(c) = child {
push_comment_and_comment_extended(
@ -806,9 +826,13 @@ fn collect_comments_in_table(
}
}
}
TableCellContent::Table(table) => {
collect_comments_in_table(table, comments, comments_extended, comment_map)
}
TableCellContent::Table(table) => collect_dependencies_in_table(
table,
comments,
comments_extended,
comment_map,
hyperlink_map,
),
}
}
}

View File

@ -0,0 +1,104 @@
use std::io::Read;
use std::str::FromStr;
use xml::attribute::OwnedAttribute;
use xml::reader::{EventReader, XmlEvent};
use super::*;
use super::attributes::*;
impl ElementReader for Hyperlink {
fn read<R: Read>(
r: &mut EventReader<R>,
attrs: &[OwnedAttribute],
) -> Result<Self, ReaderError> {
let mut rid: Option<String> = read(attrs, "id");
let mut anchor: Option<String> = read(attrs, "anchor");
let history: Option<String> = read(attrs, "history");
let mut link = Hyperlink {
link: if anchor.is_some() {
HyperlinkData::Anchor {
anchor: anchor.take().unwrap(),
}
} else {
HyperlinkData::External {
rid: rid.take().unwrap_or_default(),
path: String::default(), // not used
}
},
history: history.map(|h| usize::from_str(&h).unwrap_or(1)),
children: vec![],
};
loop {
let e = r.next();
match e {
Ok(XmlEvent::StartElement {
attributes, name, ..
}) => {
let e = XMLElement::from_str(&name.local_name).unwrap();
match e {
XMLElement::Run => {
if let Ok(run) = Run::read(r, attrs) {
link = link.add_run(run);
}
continue;
}
XMLElement::Insert => {
if let Ok(ins) = Insert::read(r, &attributes) {
link = link.add_insert(ins);
}
continue;
}
XMLElement::Delete => {
if let Ok(del) = Delete::read(r, &attributes) {
link = link.add_delete(del);
}
continue;
}
XMLElement::BookmarkStart => {
if let Ok(s) = BookmarkStart::read(r, &attributes) {
link = link.add_bookmark_start(s.id, s.name);
}
continue;
}
XMLElement::BookmarkEnd => {
if let Ok(e) = BookmarkEnd::read(r, &attributes) {
link = link.add_bookmark_end(e.id);
}
continue;
}
XMLElement::CommentRangeStart => {
if let Some(id) = read(&attributes, "id") {
if let Ok(id) = usize::from_str(&id) {
let comment = Comment::new(id);
link = link.add_comment_start(comment);
}
}
continue;
}
XMLElement::CommentRangeEnd => {
if let Some(id) = read(&attributes, "id") {
if let Ok(id) = usize::from_str(&id) {
link = link.add_comment_end(id);
}
}
continue;
}
_ => {}
}
}
Ok(XmlEvent::EndElement { name, .. }) => {
let e = XMLElement::from_str(&name.local_name).unwrap();
if e == XMLElement::Hyperlink {
return Ok(link);
}
}
Err(_) => return Err(ReaderError::XMLReadError),
_ => {}
}
}
}
}

View File

@ -10,7 +10,6 @@ mod comments_extended;
mod custom_properties;
mod delete;
mod div;
mod shape;
mod doc_defaults;
mod doc_grid;
mod document;
@ -22,6 +21,7 @@ mod font_scheme;
mod footer;
mod from_xml;
mod header;
mod hyperlink;
mod ignore;
mod insert;
mod instr_text;
@ -41,6 +41,7 @@ mod run_property;
mod section_property;
mod settings;
mod shading;
mod shape;
mod structured_data_tag;
mod style;
mod styles;

View File

@ -27,10 +27,15 @@ impl ElementReader for Paragraph {
match e {
XMLElement::Run => {
let run = Run::read(r, attrs)?;
let run = Run::read(r, &attributes)?;
p = p.add_run(run);
continue;
}
XMLElement::Hyperlink => {
let link = Hyperlink::read(r, &attributes)?;
p = p.add_hyperlink(link);
continue;
}
XMLElement::Insert => {
let ins = Insert::read(r, &attributes)?;
p = p.add_insert(ins);
@ -70,7 +75,7 @@ impl ElementReader for Paragraph {
}
// pPr
XMLElement::ParagraphProperty => {
if let Ok(pr) = ParagraphProperty::read(r, attrs) {
if let Ok(pr) = ParagraphProperty::read(r, &attributes) {
p.has_numbering = pr.numbering_property.is_some();
p.property = pr;
}

View File

@ -26,6 +26,7 @@ pub enum XMLElement {
Text,
FieldChar,
InstrText,
Hyperlink,
Highlight,
VertAlign,
Bold,
@ -240,6 +241,7 @@ impl FromStr for XMLElement {
"t" => Ok(XMLElement::Text),
"fldChar" => Ok(XMLElement::FieldChar),
"instrText" => Ok(XMLElement::InstrText),
"hyperlink" => Ok(XMLElement::Hyperlink),
"sz" => Ok(XMLElement::Size),
"szCs" => Ok(XMLElement::SizeCs),
"u" => Ok(XMLElement::Underline),

View File

@ -0,0 +1,36 @@
use std::fmt;
use wasm_bindgen::prelude::*;
use serde::Serialize;
use super::errors;
use std::str::FromStr;
#[wasm_bindgen]
#[derive(Debug, Clone, PartialEq, Serialize, ts_rs::TS)]
#[serde(rename_all = "camelCase")]
#[ts(export)]
pub enum HyperlinkType {
Anchor,
External,
}
impl fmt::Display for HyperlinkType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
HyperlinkType::Anchor => write!(f, "anchor"),
HyperlinkType::External => write!(f, "external"),
}
}
}
impl FromStr for HyperlinkType {
type Err = errors::TypeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"anchor" => Ok(HyperlinkType::Anchor),
"external" => Ok(HyperlinkType::External),
_ => Ok(HyperlinkType::Anchor),
}
}
}

View File

@ -9,6 +9,7 @@ pub mod errors;
pub mod field_char_type;
pub mod font_pitch_type;
pub mod height_rule;
pub mod hyperlink_type;
pub mod level_suffix_type;
pub mod line_spacing_type;
pub mod page_margin;
@ -39,6 +40,7 @@ pub use errors::*;
pub use field_char_type::*;
pub use font_pitch_type::*;
pub use height_rule::*;
pub use hyperlink_type::*;
pub use level_suffix_type::*;
pub use line_spacing_type::*;
pub use page_margin::*;

View File

@ -106,18 +106,18 @@ impl XMLBuilder {
mut self,
rid: Option<&String>,
anchor: Option<&String>,
history: bool,
history: Option<usize>,
) -> Self {
let mut e = XmlEvent::start_element("w:hyperlink");
let history = history.unwrap_or(1);
if let Some(rid) = rid {
e = e.attr("w:rid", rid);
e = e.attr("r:id", rid);
}
if let Some(anchor) = anchor {
e = e.attr("w:anchor", anchor);
}
if history {
e = e.attr("w:history", "true");
}
let s = format!("{}", history);
e = e.attr("w:history", s.as_str());
self.writer.write(e).expect(EXPECT_MESSAGE);
self
}

View File

@ -8,6 +8,14 @@ impl XMLBuilder {
// Build Relationship
closed!(relationship, "Relationship", "Id", "Type", "Target");
closed!(
relationship_with_mode,
"Relationship",
"Id",
"Type",
"Target",
"TargetMode"
);
}
#[cfg(test)]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,5 @@
import * as wasm from "./pkg";
import { Run } from "./run";
import { Insert } from "./insert";
import { Delete } from "./delete";
@ -7,12 +9,18 @@ import { Comment } from "./comment";
import { CommentEnd } from "./comment-end";
import { ParagraphChild } from "./paragraph";
export type HyperlinkType = "anchor" | "external";
export class Hyperlink {
_rid?: string;
_anchor?: string;
_history: boolean = false;
v: string;
type: HyperlinkType;
children: ParagraphChild[] = [];
constructor(v: string, t: HyperlinkType) {
this.v = v;
this.type = t;
}
addRun(run: Run) {
this.children.push(run);
return this;
@ -47,19 +55,11 @@ export class Hyperlink {
this.children.push(end);
return this;
}
rid(rid: string) {
this._rid = rid;
return this;
}
anchor(anchor: string) {
this._anchor = anchor;
return this;
}
history() {
this._history = true;
return this;
}
}
export const convertHyperlinkType = (link: Hyperlink): wasm.HyperlinkType => {
if (link.type === "anchor") {
return wasm.HyperlinkType.Anchor;
}
return wasm.HyperlinkType.External;
};

View File

@ -2,7 +2,7 @@ import { Paragraph } from "./paragraph";
import { ParagraphProperty } from "./paragraph-property";
import { Insert } from "./insert";
import { Delete } from "./delete";
import { Hyperlink } from "./hyperlink";
import { convertHyperlinkType, Hyperlink } from "./hyperlink";
import { DeleteText } from "./delete-text";
import { Table } from "./table";
import { TableOfContents } from "./table-of-contents";
@ -372,16 +372,7 @@ export class Docx {
}
buildHyperlink(link: Hyperlink) {
let hyperlink = wasm.createHyperlink();
if (link._history) {
hyperlink = hyperlink.history();
}
if (link._anchor) {
hyperlink = hyperlink.anchor(link._anchor);
}
if (link._rid) {
hyperlink = hyperlink.rid(link._rid);
}
let hyperlink = wasm.createHyperlink(link.v, convertHyperlinkType(link));
link.children.forEach((child) => {
if (child instanceof Run) {

View File

@ -0,0 +1,2 @@
export type HyperlinkType = "anchor" | "external";

View File

@ -4,6 +4,16 @@ import { CommentRangeStartJSON, CommentRangeEndJSON } from "..";
import { LineSpacingJSON } from "./line_spacing";
export type ParagraphChildJSON =
| RunJSON
| InsertJSON
| DeleteJSON
| HyperlinkJSON
| CommentRangeStartJSON
| CommentRangeEndJSON
| BookmarkStartJSON
| BookmarkEndJSON;
export type HyperlinkChildJSON =
| RunJSON
| InsertJSON
| DeleteJSON
@ -69,6 +79,21 @@ export type DeleteJSON = {
};
};
export type HyperlinkJSON = {
type: "hyperlink";
data:
| {
type: "external";
rid: string;
children: HyperlinkChildJSON[];
history: number | null;
}
| {
type: "anchor";
anchor: string;
};
};
export type DeleteChildJSON =
| RunJSON
| CommentRangeStartJSON

View File

@ -7,27 +7,12 @@ use super::*;
pub struct Hyperlink(docx_rs::Hyperlink);
#[wasm_bindgen(js_name = createHyperlink)]
pub fn create_hyperlink() -> Hyperlink {
Hyperlink(docx_rs::Hyperlink::new())
pub fn create_hyperlink(v: &str, t: docx_rs::HyperlinkType) -> Hyperlink {
Hyperlink(docx_rs::Hyperlink::new(v, t))
}
#[wasm_bindgen]
impl Hyperlink {
pub fn rid(mut self, rid: &str) -> Self {
self.0 = self.0.rid(rid);
self
}
pub fn anchor(mut self, anchor: &str) -> Self {
self.0 = self.0.anchor(anchor);
self
}
pub fn history(mut self) -> Self {
self.0 = self.0.history();
self
}
pub fn add_run(mut self, run: Run) -> Self {
self.0 = self.0.add_run(run.take());
self

File diff suppressed because it is too large Load Diff

View File

@ -153,6 +153,12 @@ describe("reader", () => {
const json = w.readDocx(buffer);
expect(json).toMatchSnapshot();
});
test("should read hyperlink", () => {
const buffer = readFileSync("../fixtures/link/link.docx");
const json = w.readDocx(buffer);
expect(json).toMatchSnapshot();
});
});
describe("writer", () => {
@ -518,9 +524,9 @@ describe("writer", () => {
}
});
test("should write hyperlink", () => {
test("should write anchor hyperlink", () => {
const p1 = new w.Paragraph().addHyperlink(
new w.Hyperlink().anchor("anchor").addRun(new w.Run().addText("Hello!!"))
new w.Hyperlink("anchor", "anchor").addRun(new w.Run().addText("Hello!!"))
);
const p2 = new w.Paragraph()
.addBookmarkStart(1, "anchor")
@ -529,7 +535,24 @@ describe("writer", () => {
.addBookmarkEnd(1);
const buffer = new w.Docx().addParagraph(p1).addParagraph(p2).build();
writeFileSync("../output/js/hyperlink.docx", buffer);
writeFileSync("../output/js/anchor-hyperlink.docx", buffer);
const z = new Zip(Buffer.from(buffer));
for (const e of z.getEntries()) {
if (e.entryName.match(/document.xml/)) {
expect(z.readAsText(e)).toMatchSnapshot();
}
}
});
test("should write external hyperlink", () => {
const p1 = new w.Paragraph().addHyperlink(
new w.Hyperlink("https://example.com", "external").addRun(
new w.Run().addText("Hello!!")
)
);
const buffer = new w.Docx().addParagraph(p1).build();
writeFileSync("../output/js/external-hyperlink.docx", buffer);
const z = new Zip(Buffer.from(buffer));
for (const e of z.getEntries()) {
if (e.entryName.match(/document.xml/)) {

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/><Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/><Override PartName="/word/settings.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"/><Override PartName="/word/webSettings.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml"/><Override PartName="/word/fontTable.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml"/><Override PartName="/word/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/><Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/></Types>

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/></Relationships>

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><Template>Normal.dotm</Template><TotalTime>1</TotalTime><Pages>1</Pages><Words>6</Words><Characters>37</Characters><Application>Microsoft Office Word</Application><DocSecurity>0</DocSecurity><Lines>1</Lines><Paragraphs>1</Paragraphs><ScaleCrop>false</ScaleCrop><Company></Company><LinksUpToDate>false</LinksUpToDate><CharactersWithSpaces>42</CharactersWithSpaces><SharedDoc>false</SharedDoc><HyperlinksChanged>false</HyperlinksChanged><AppVersion>16.0000</AppVersion></Properties>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:dcterms="http://purl.org/dc/terms/"
xmlns:dcmitype="http://purl.org/dc/dcmitype/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<dc:title></dc:title>
<dc:subject></dc:subject>
<dc:creator>bokuweb</dc:creator>
<cp:keywords></cp:keywords>
<dc:description></dc:description>
<cp:lastModifiedBy>bokuweb</cp:lastModifiedBy>
<cp:revision>1</cp:revision>
<dcterms:created xsi:type="dcterms:W3CDTF">2022-06-30T14:30:00Z</dcterms:created>
<dcterms:modified xsi:type="dcterms:W3CDTF">2022-06-30T14:31:00Z</dcterms:modified>
</cp:coreProperties>

Binary file not shown.

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings" Target="webSettings.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" Target="settings.xml"/><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/><Relationship Id="rId6" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/><Relationship Id="rId5" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable" Target="fontTable.xml"/><Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="https://google.com/" TargetMode="External"/></Relationships>

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas"
xmlns:cx="http://schemas.microsoft.com/office/drawing/2014/chartex"
xmlns:cx1="http://schemas.microsoft.com/office/drawing/2015/9/8/chartex"
xmlns:cx2="http://schemas.microsoft.com/office/drawing/2015/10/21/chartex"
xmlns:cx3="http://schemas.microsoft.com/office/drawing/2016/5/9/chartex"
xmlns:cx4="http://schemas.microsoft.com/office/drawing/2016/5/10/chartex"
xmlns:cx5="http://schemas.microsoft.com/office/drawing/2016/5/11/chartex"
xmlns:cx6="http://schemas.microsoft.com/office/drawing/2016/5/12/chartex"
xmlns:cx7="http://schemas.microsoft.com/office/drawing/2016/5/13/chartex"
xmlns:cx8="http://schemas.microsoft.com/office/drawing/2016/5/14/chartex"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:aink="http://schemas.microsoft.com/office/drawing/2016/ink"
xmlns:am3d="http://schemas.microsoft.com/office/drawing/2017/model3d"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:oel="http://schemas.microsoft.com/office/2019/extlst"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math"
xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing"
xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"
xmlns:w10="urn:schemas-microsoft-com:office:word"
xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml"
xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml"
xmlns:w16cex="http://schemas.microsoft.com/office/word/2018/wordml/cex"
xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid"
xmlns:w16="http://schemas.microsoft.com/office/word/2018/wordml"
xmlns:w16sdtdh="http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash"
xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex"
xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup"
xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk"
xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml"
xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="w14 w15 w16se w16cid w16 w16cex w16sdtdh wp14">
<w:body>
<w:p w14:paraId="6B7AF587" w14:textId="1D3B7D02" w:rsidR="00125C12" w:rsidRDefault="004E7262">
<w:hyperlink r:id="rId4">
<w:r w:rsidRPr="004E7262">
<w:rPr>
</w:rPr>
<w:t>Hello</w:t>
</w:r>
</w:hyperlink>
<w:r>
<w:t>o</w:t>
</w:r>
</w:p>
<w:sectPr w:rsidR="00125C12">
<w:pgSz w:w="11906" w:h="16838"/>
<w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0"/>
<w:cols w:space="425"/>
<w:docGrid w:type="lines" w:linePitch="360"/>
</w:sectPr>
</w:body>
</w:document>

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:fonts xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cex="http://schemas.microsoft.com/office/word/2018/wordml/cex" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16="http://schemas.microsoft.com/office/word/2018/wordml" xmlns:w16sdtdh="http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" mc:Ignorable="w14 w15 w16se w16cid w16 w16cex w16sdtdh"><w:font w:name="游明朝"><w:panose1 w:val="02020400000000000000"/><w:charset w:val="80"/><w:family w:val="roman"/><w:pitch w:val="variable"/><w:sig w:usb0="800002E7" w:usb1="2AC7FCFF" w:usb2="00000012" w:usb3="00000000" w:csb0="0002009F" w:csb1="00000000"/></w:font><w:font w:name="Times New Roman"><w:panose1 w:val="02020603050405020304"/><w:charset w:val="00"/><w:family w:val="roman"/><w:pitch w:val="variable"/><w:sig w:usb0="E0002EFF" w:usb1="C000785B" w:usb2="00000009" w:usb3="00000000" w:csb0="000001FF" w:csb1="00000000"/></w:font><w:font w:name="游ゴシック Light"><w:panose1 w:val="020B0300000000000000"/><w:charset w:val="80"/><w:family w:val="swiss"/><w:pitch w:val="variable"/><w:sig w:usb0="E00002FF" w:usb1="2AC7FDFF" w:usb2="00000016" w:usb3="00000000" w:csb0="0002009F" w:csb1="00000000"/></w:font></w:fonts>

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:settings xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cex="http://schemas.microsoft.com/office/word/2018/wordml/cex" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16="http://schemas.microsoft.com/office/word/2018/wordml" xmlns:w16sdtdh="http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" xmlns:sl="http://schemas.openxmlformats.org/schemaLibrary/2006/main" mc:Ignorable="w14 w15 w16se w16cid w16 w16cex w16sdtdh"><w:zoom w:percent="224"/><w:doNotDisplayPageBoundaries/><w:bordersDoNotSurroundHeader/><w:bordersDoNotSurroundFooter/><w:proofState w:grammar="clean"/><w:defaultTabStop w:val="840"/><w:displayHorizontalDrawingGridEvery w:val="0"/><w:displayVerticalDrawingGridEvery w:val="2"/><w:characterSpacingControl w:val="compressPunctuation"/><w:compat><w:spaceForUL/><w:balanceSingleByteDoubleByteWidth/><w:doNotLeaveBackslashAlone/><w:ulTrailSpace/><w:doNotExpandShiftReturn/><w:adjustLineHeightInTable/><w:useFELayout/><w:compatSetting w:name="compatibilityMode" w:uri="http://schemas.microsoft.com/office/word" w:val="15"/><w:compatSetting w:name="overrideTableStyleFontSizeAndJustification" w:uri="http://schemas.microsoft.com/office/word" w:val="1"/><w:compatSetting w:name="enableOpenTypeFeatures" w:uri="http://schemas.microsoft.com/office/word" w:val="1"/><w:compatSetting w:name="doNotFlipMirrorIndents" w:uri="http://schemas.microsoft.com/office/word" w:val="1"/><w:compatSetting w:name="differentiateMultirowTableHeaders" w:uri="http://schemas.microsoft.com/office/word" w:val="1"/><w:compatSetting w:name="useWord2013TrackBottomHyphenation" w:uri="http://schemas.microsoft.com/office/word" w:val="0"/></w:compat><w:rsids><w:rsidRoot w:val="004E7262"/><w:rsid w:val="00125C12"/><w:rsid w:val="004E7262"/><w:rsid w:val="00B26F0E"/><w:rsid w:val="00CE04E0"/></w:rsids><m:mathPr><m:mathFont m:val="Cambria Math"/><m:brkBin m:val="before"/><m:brkBinSub m:val="--"/><m:smallFrac m:val="0"/><m:dispDef/><m:lMargin m:val="0"/><m:rMargin m:val="0"/><m:defJc m:val="centerGroup"/><m:wrapIndent m:val="1440"/><m:intLim m:val="subSup"/><m:naryLim m:val="undOvr"/></m:mathPr><w:themeFontLang w:val="en-US" w:eastAsia="ja-JP"/><w:clrSchemeMapping w:bg1="light1" w:t1="dark1" w:bg2="light2" w:t2="dark2" w:accent1="accent1" w:accent2="accent2" w:accent3="accent3" w:accent4="accent4" w:accent5="accent5" w:accent6="accent6" w:hyperlink="hyperlink" w:followedHyperlink="followedHyperlink"/><w:decimalSymbol w:val="."/><w:listSeparator w:val=","/><w14:docId w14:val="55AA446F"/><w15:chartTrackingRefBased/><w15:docId w15:val="{B1317C39-ACCF-5B4D-BE97-FD2D73F14B00}"/></w:settings>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:webSettings xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cex="http://schemas.microsoft.com/office/word/2018/wordml/cex" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16="http://schemas.microsoft.com/office/word/2018/wordml" xmlns:w16sdtdh="http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" mc:Ignorable="w14 w15 w16se w16cid w16 w16cex w16sdtdh"><w:optimizeForBrowser/><w:allowPNG/></w:webSettings>