mirror of
https://github.com/typst/typst
synced 2025-08-15 07:28:32 +08:00
Fix page range issue
This commit is contained in:
parent
27c5e0081a
commit
9b5ee82c16
@ -52,10 +52,13 @@ pub fn convert(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut document = Document::new_with(settings);
|
let mut document = Document::new_with(settings);
|
||||||
|
let page_index_converter = PageIndexConverter::new(&typst_document, &options);
|
||||||
|
let named_destinations = collect_named_destinations(&typst_document, &page_index_converter);
|
||||||
let mut gc = GlobalContext::new(
|
let mut gc = GlobalContext::new(
|
||||||
typst_document,
|
typst_document,
|
||||||
options,
|
options,
|
||||||
collect_named_destinations(typst_document, options),
|
named_destinations,
|
||||||
|
page_index_converter
|
||||||
);
|
);
|
||||||
|
|
||||||
convert_pages(&mut gc, &mut document)?;
|
convert_pages(&mut gc, &mut document)?;
|
||||||
@ -68,12 +71,9 @@ pub fn convert(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn convert_pages(gc: &mut GlobalContext, document: &mut Document) -> SourceResult<()> {
|
fn convert_pages(gc: &mut GlobalContext, document: &mut Document) -> SourceResult<()> {
|
||||||
let mut skipped_pages = 0;
|
|
||||||
|
|
||||||
for (i, typst_page) in gc.document.pages.iter().enumerate() {
|
for (i, typst_page) in gc.document.pages.iter().enumerate() {
|
||||||
if gc.page_excluded(i) {
|
if gc.page_index_converter.pdf_page_index(i).is_none() {
|
||||||
// Don't export this page.
|
// Don't export this page.
|
||||||
skipped_pages += 1;
|
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
let mut settings = PageSettings::new(
|
let mut settings = PageSettings::new(
|
||||||
@ -93,7 +93,7 @@ fn convert_pages(gc: &mut GlobalContext, document: &mut Document) -> SourceResul
|
|||||||
// the real (not logical) page numbers. Here, the final PDF page number
|
// the real (not logical) page numbers. Here, the final PDF page number
|
||||||
// will differ, but we can at least use labels to indicate what was
|
// will differ, but we can at least use labels to indicate what was
|
||||||
// the corresponding real page number in the Typst document.
|
// the corresponding real page number in the Typst document.
|
||||||
(skipped_pages > 0).then(|| PageLabel::arabic(i + 1))
|
gc.page_index_converter.has_skipped_pages().then(|| PageLabel::arabic(i + 1))
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
settings = settings.with_page_label(label);
|
settings = settings.with_page_label(label);
|
||||||
@ -219,35 +219,31 @@ pub(crate) struct GlobalContext<'a> {
|
|||||||
/// Options for PDF export.
|
/// Options for PDF export.
|
||||||
pub(crate) options: &'a PdfOptions<'a>,
|
pub(crate) options: &'a PdfOptions<'a>,
|
||||||
/// Mapping between locations in the document and named destinations.
|
/// Mapping between locations in the document and named destinations.
|
||||||
pub(crate) loc_to_named: HashMap<Location, NamedDestination>,
|
pub(crate) loc_to_names: HashMap<Location, NamedDestination>,
|
||||||
/// The languages used throughout the document.
|
/// The languages used throughout the document.
|
||||||
pub(crate) languages: BTreeMap<Lang, usize>,
|
pub(crate) languages: BTreeMap<Lang, usize>,
|
||||||
|
pub(crate) page_index_converter: PageIndexConverter
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> GlobalContext<'a> {
|
impl<'a> GlobalContext<'a> {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
document: &'a PagedDocument,
|
document: &'a PagedDocument,
|
||||||
options: &'a PdfOptions,
|
options: &'a PdfOptions,
|
||||||
loc_to_named: HashMap<Location, NamedDestination>,
|
loc_to_names: HashMap<Location, NamedDestination>,
|
||||||
|
page_index_converter: PageIndexConverter
|
||||||
) -> GlobalContext<'a> {
|
) -> GlobalContext<'a> {
|
||||||
Self {
|
Self {
|
||||||
fonts_forward: HashMap::new(),
|
fonts_forward: HashMap::new(),
|
||||||
fonts_backward: HashMap::new(),
|
fonts_backward: HashMap::new(),
|
||||||
document,
|
document,
|
||||||
options,
|
options,
|
||||||
loc_to_named,
|
loc_to_names,
|
||||||
image_to_spans: HashMap::new(),
|
image_to_spans: HashMap::new(),
|
||||||
image_spans: HashSet::new(),
|
image_spans: HashSet::new(),
|
||||||
languages: BTreeMap::new(),
|
languages: BTreeMap::new(),
|
||||||
|
page_index_converter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn page_excluded(&self, page_index: usize) -> bool {
|
|
||||||
self.options
|
|
||||||
.page_ranges
|
|
||||||
.as_ref()
|
|
||||||
.is_some_and(|ranges| !ranges.includes_page_index(page_index))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn handle_frame(
|
pub(crate) fn handle_frame(
|
||||||
@ -556,7 +552,7 @@ fn finish(
|
|||||||
|
|
||||||
fn collect_named_destinations(
|
fn collect_named_destinations(
|
||||||
document: &PagedDocument,
|
document: &PagedDocument,
|
||||||
options: &PdfOptions,
|
pic: &PageIndexConverter
|
||||||
) -> HashMap<Location, NamedDestination> {
|
) -> HashMap<Location, NamedDestination> {
|
||||||
let mut locs_to_names = HashMap::new();
|
let mut locs_to_names = HashMap::new();
|
||||||
|
|
||||||
@ -582,11 +578,7 @@ fn collect_named_destinations(
|
|||||||
let y = (pos.point.y - Abs::pt(10.0)).max(Abs::zero());
|
let y = (pos.point.y - Abs::pt(10.0)).max(Abs::zero());
|
||||||
|
|
||||||
// Only add named destination if page belonging to the position is exported.
|
// Only add named destination if page belonging to the position is exported.
|
||||||
if options
|
if let Some(index) = pic.pdf_page_index(index) {
|
||||||
.page_ranges
|
|
||||||
.as_ref()
|
|
||||||
.is_some_and(|ranges| !ranges.includes_page_index(index))
|
|
||||||
{
|
|
||||||
let named = NamedDestination::new(
|
let named = NamedDestination::new(
|
||||||
label.resolve().to_string(),
|
label.resolve().to_string(),
|
||||||
XyzDestination::new(
|
XyzDestination::new(
|
||||||
@ -631,3 +623,38 @@ fn get_configuration(options: &PdfOptions) -> SourceResult<Configuration> {
|
|||||||
|
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) struct PageIndexConverter {
|
||||||
|
page_indices: HashMap<usize, usize>,
|
||||||
|
skipped_pages: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PageIndexConverter {
|
||||||
|
pub fn new(document: &PagedDocument, options: &PdfOptions) -> Self {
|
||||||
|
let mut page_indices = HashMap::new();
|
||||||
|
let mut skipped_pages = 0;
|
||||||
|
|
||||||
|
for i in 0..document.pages.len() {
|
||||||
|
if options
|
||||||
|
.page_ranges
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|ranges| !ranges.includes_page_index(i))
|
||||||
|
{
|
||||||
|
skipped_pages += 1;
|
||||||
|
} else {
|
||||||
|
page_indices.insert(i, i - skipped_pages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self { page_indices, skipped_pages }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn has_skipped_pages(&self) -> bool {
|
||||||
|
self.skipped_pages > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the PDF page index of a page index, if it's not excluded.
|
||||||
|
pub(crate) fn pdf_page_index(&self, page_index: usize) -> Option<usize> {
|
||||||
|
self.page_indices.get(&page_index).copied()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -58,7 +58,7 @@ pub(crate) fn handle_link(
|
|||||||
}
|
}
|
||||||
Destination::Position(p) => *p,
|
Destination::Position(p) => *p,
|
||||||
Destination::Location(loc) => {
|
Destination::Location(loc) => {
|
||||||
if let Some(nd) = gc.loc_to_named.get(loc) {
|
if let Some(nd) = gc.loc_to_names.get(loc) {
|
||||||
// If a named destination has been registered, it's already guaranteed to
|
// If a named destination has been registered, it's already guaranteed to
|
||||||
// not point to an excluded page.
|
// not point to an excluded page.
|
||||||
fc.push_annotation(
|
fc.push_annotation(
|
||||||
@ -79,13 +79,13 @@ pub(crate) fn handle_link(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let page_index = pos.page.get() - 1;
|
let page_index = pos.page.get() - 1;
|
||||||
if !gc.page_excluded(page_index) {
|
if let Some(index) = gc.page_index_converter.pdf_page_index(page_index) {
|
||||||
fc.push_annotation(
|
fc.push_annotation(
|
||||||
LinkAnnotation::new(
|
LinkAnnotation::new(
|
||||||
rect,
|
rect,
|
||||||
None,
|
None,
|
||||||
Target::Destination(krilla::destination::Destination::Xyz(
|
Target::Destination(krilla::destination::Destination::Xyz(
|
||||||
XyzDestination::new(page_index, pos.point.to_krilla()),
|
XyzDestination::new(index, pos.point.to_krilla()),
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
|
@ -127,10 +127,10 @@ impl<'a> HeadingNode<'a> {
|
|||||||
let pos = gc.document.introspector.position(loc);
|
let pos = gc.document.introspector.position(loc);
|
||||||
let page_index = pos.page.get() - 1;
|
let page_index = pos.page.get() - 1;
|
||||||
|
|
||||||
if !gc.page_excluded(page_index) {
|
if let Some(index) = gc.page_index_converter.pdf_page_index(page_index) {
|
||||||
let y = (pos.point.y - Abs::pt(10.0)).max(Abs::zero());
|
let y = (pos.point.y - Abs::pt(10.0)).max(Abs::zero());
|
||||||
let dest = XyzDestination::new(
|
let dest = XyzDestination::new(
|
||||||
page_index,
|
index,
|
||||||
krilla::geom::Point::from_xy(pos.point.x.to_f32(), y.to_f32()),
|
krilla::geom::Point::from_xy(pos.point.x.to_f32(), y.to_f32()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user