mirror of
https://github.com/johnwhitington/cpdf-source.git
synced 2025-02-05 12:58:39 +01:00
more
This commit is contained in:
parent
148cde595d
commit
4e3072803a
3
Makefile
3
Makefile
@ -7,7 +7,8 @@ DOC = cpdfunicodedata cpdferror cpdfdebug cpdfjson cpdfstrftime cpdfcoord \
|
||||
cpdfembed cpdfaddtext cpdffont cpdftype cpdfpad cpdfocg \
|
||||
cpdfsqueeze cpdfdraft cpdfspot cpdfpagelabels cpdfcreate cpdfannot \
|
||||
cpdfxobject cpdfimpose cpdftweak cpdftexttopdf cpdftoc cpdfjpeg \
|
||||
cpdfpng cpdfimage cpdfdraw cpdfcomposition cpdfcommand
|
||||
cpdfpng cpdfimage cpdfdraw cpdfcomposition cpdfgraphics cpdfshape \
|
||||
cpdfcommand
|
||||
|
||||
MODS = $(NONDOC) $(DOC)
|
||||
|
||||
|
@ -1832,14 +1832,14 @@ let addbezier s =
|
||||
let addcircle s =
|
||||
match readfloats s with
|
||||
| [x; y; r] ->
|
||||
let _, _, segs = hd (snd (Pdfshapes.circle x y r)) in
|
||||
let _, _, segs = hd (snd (Cpdfshape.circle x y r)) in
|
||||
(match segs with
|
||||
| Pdfgraphics.Bezier ((a, b), _, _, _)::_ -> addop (Cpdfdraw.To (a, b))
|
||||
| Cpdfgraphics.Bezier ((a, b), _, _, _)::_ -> addop (Cpdfdraw.To (a, b))
|
||||
| _ -> assert false);
|
||||
iter
|
||||
(function
|
||||
| Pdfgraphics.Bezier (_, (c, d), (e, f), (g, h)) -> addop (Cpdfdraw.Bezier (c, d, e, f, g, h))
|
||||
| Pdfgraphics.Straight _ -> assert false)
|
||||
| Cpdfgraphics.Bezier (_, (c, d), (e, f), (g, h)) -> addop (Cpdfdraw.Bezier (c, d, e, f, g, h))
|
||||
| Cpdfgraphics.Straight _ -> assert false)
|
||||
segs
|
||||
| _ -> error "-circle requires three numbers"
|
||||
| exception _ -> error "malformed -circle"
|
||||
|
1745
cpdfgraphics.ml
Normal file
1745
cpdfgraphics.ml
Normal file
File diff suppressed because it is too large
Load Diff
172
cpdfgraphics.mli
Normal file
172
cpdfgraphics.mli
Normal file
@ -0,0 +1,172 @@
|
||||
(** Structured Graphics. This will (eventually) be a module allowing for the raising of a page's contents to a tree form, the manipulation of that tree and its writing back to the page, with no possible loss of fidelity.
|
||||
|
||||
It is only a little experiment at the moment... *)
|
||||
|
||||
open Pdfutil
|
||||
open Pdfio
|
||||
|
||||
(** Point. *)
|
||||
type fpoint = float * float
|
||||
|
||||
(** Winding rule. *)
|
||||
type winding_rule = EvenOdd | NonZero
|
||||
|
||||
(** A segment (a straight line or bezier curve) *)
|
||||
type segment =
|
||||
| Straight of fpoint * fpoint
|
||||
| Bezier of fpoint * fpoint * fpoint * fpoint
|
||||
|
||||
(** Each segment list may be marked as a hole or not. *)
|
||||
type hole = Hole | Not_hole
|
||||
|
||||
(* A [subpath] is either closed or open. *)
|
||||
type closure = Closed | Open
|
||||
|
||||
(** A [subpath] is the pair of a hole and a list of segments. *)
|
||||
type subpath = hole * closure * segment list
|
||||
|
||||
(** A path is made from a number of subpaths. *)
|
||||
type path = winding_rule * subpath list
|
||||
|
||||
val string_of_path : path -> string
|
||||
|
||||
(** Colour values *)
|
||||
type tiling = Tiling
|
||||
|
||||
type function_shading =
|
||||
{funshading_domain : float * float * float * float;
|
||||
funshading_matrix : Pdftransform.transform_matrix;
|
||||
funshading_function : Pdffun.t}
|
||||
|
||||
type radial_shading =
|
||||
{radialshading_coords : float * float * float * float * float * float;
|
||||
radialshading_domain : float * float;
|
||||
radialshading_function : Pdffun.t list;
|
||||
radialshading_extend : bool * bool}
|
||||
|
||||
type axial_shading =
|
||||
{axialshading_coords : float * float * float * float;
|
||||
axialshading_domain : float * float;
|
||||
axialshading_function : Pdffun.t list;
|
||||
axialshading_extend : bool * bool}
|
||||
|
||||
type shading_kind =
|
||||
| FunctionShading of function_shading
|
||||
| AxialShading of axial_shading
|
||||
| RadialShading of radial_shading
|
||||
| FreeFormGouraudShading
|
||||
| LatticeFormGouraudShading
|
||||
| CoonsPatchMesh
|
||||
| TensorProductPatchMesh
|
||||
|
||||
type shading =
|
||||
{shading_colourspace : Pdf.pdfobject;
|
||||
shading_background : Pdf.pdfobject option;
|
||||
shading_bbox : Pdf.pdfobject option;
|
||||
shading_antialias : bool;
|
||||
shading_matrix : Pdftransform.transform_matrix;
|
||||
shading_extgstate : Pdf.pdfobject;
|
||||
shading : shading_kind}
|
||||
|
||||
type pattern =
|
||||
| ColouredTilingPattern of tiling
|
||||
| UncolouredTilingPattern of tiling
|
||||
| ShadingPattern of shading
|
||||
|
||||
type colvals =
|
||||
| Floats of float list
|
||||
| Named of (string * float list)
|
||||
| Pattern of pattern
|
||||
|
||||
type transparency_attributes =
|
||||
{fill_transparency : float;
|
||||
line_transparency : float}
|
||||
|
||||
(** Path attributes. *)
|
||||
type path_attributes =
|
||||
{path_transform : Pdftransform.transform_matrix;
|
||||
path_fill : (Pdfspace.t * colvals) option;
|
||||
path_line : (Pdfspace.t * colvals) option;
|
||||
path_linewidth : float;
|
||||
path_joinstyle : int;
|
||||
path_capstyle : int;
|
||||
path_dash : float list * float;
|
||||
path_mitrelimit : float;
|
||||
path_transparency : transparency_attributes;
|
||||
path_intent : string}
|
||||
|
||||
type text_attributes =
|
||||
{textmode : int}
|
||||
|
||||
type textblock_attributes =
|
||||
{textblock_transform : Pdftransform.transform_matrix}
|
||||
|
||||
type textblock =
|
||||
text_attributes * Pdfops.t
|
||||
|
||||
type image_attributes =
|
||||
{image_transform : Pdftransform.transform_matrix;
|
||||
image_transparency : float;
|
||||
image_softmask : softmask option} (* The /ca value *)
|
||||
|
||||
and softmask_subtype =
|
||||
Alpha | Luminosity
|
||||
|
||||
and transparency_group =
|
||||
{tr_group_colourspace : Pdf.pdfobject option;
|
||||
isolated : bool;
|
||||
knockout : bool;
|
||||
tr_graphic : t}
|
||||
|
||||
and softmask =
|
||||
{softmask_subtype : softmask_subtype;
|
||||
transparency_group : transparency_group;
|
||||
softmask_bbox : float * float * float * float;
|
||||
backdrop : float list option;
|
||||
softmask_transfer : Pdffun.t option}
|
||||
|
||||
and fontname = string * Pdf.pdfobject
|
||||
|
||||
(** For now, just support for reading paths out. Eventually a tree-structure for
|
||||
an op stream. *)
|
||||
and graphic_elt =
|
||||
| Path of (path * path_attributes)
|
||||
| Text of textblock list * textblock_attributes
|
||||
| MCPoint of string
|
||||
| MCPointProperties of string * Pdf.pdfobject
|
||||
| MCSection of string * graphic_elt list
|
||||
| MCSectionProperties of string * Pdf.pdfobject * graphic_elt list
|
||||
| Image of image_attributes * int
|
||||
| GraphicInlineImage of Pdf.pdfobject * bytes * Pdftransform.transform_matrix
|
||||
| Clip of path * graphic_elt list
|
||||
| Shading of path option * shading * Pdftransform.transform_matrix
|
||||
|
||||
and t =
|
||||
{elements : graphic_elt list; (* Page content *)
|
||||
fonts : fontname list; (* Fonts *)
|
||||
resources : Pdf.pdfobject} (* Anything else in /Resources *)
|
||||
|
||||
(** Bounding box xmin, xmax, ymin, yman of a graphic *)
|
||||
val bbox_of_graphic : t -> float * float * float * float
|
||||
|
||||
(** Make a graphic from operations. *)
|
||||
val graphic_of_page : Pdf.t -> Pdfpage.t -> t
|
||||
|
||||
(** Make a graphic from a simple string. *)
|
||||
val graphic_of_ops : Pdfops.t list -> t
|
||||
|
||||
(** Flatten a graphic to a list of operations and replace the operations in a
|
||||
page by them, returning the new page. *)
|
||||
val page_of_graphic : Pdf.t -> (float * float * float * float) -> t -> Pdfpage.t
|
||||
|
||||
(** Debug string of a [graphic] *)
|
||||
val string_of_graphic : t -> string
|
||||
|
||||
(** Operations from a simple graphic (i.e no need for resources etc.) *)
|
||||
val ops_of_simple_graphic : t -> Pdfops.t list
|
||||
|
||||
(** Pdfdoc.content entry from a simple graphic (i.e no need for resources etc.) *)
|
||||
val streams_of_simple_graphic : t -> Pdf.pdfobject list
|
||||
|
||||
(** Transform a graphic by a matrix. *)
|
||||
val transform_graphic : Pdftransform.transform_matrix -> t -> t
|
146
cpdfshape.ml
Normal file
146
cpdfshape.ml
Normal file
@ -0,0 +1,146 @@
|
||||
(* \chaptertitle{Shapes}{Stroking lines and making shapes} *)
|
||||
|
||||
(* This module provides for the stroking of lines, and production of shape
|
||||
primitives (circles, regular polygons etc). *)
|
||||
open Pdfutil
|
||||
|
||||
(* \section{Common geometric functions} *)
|
||||
|
||||
(* The factor by which we multiply the radius to find the length of the bezier
|
||||
control lines when approximating quarter arcs to make semicircles and circles.
|
||||
*)
|
||||
let kappa = ((sqrt 2. -. 1.) /. 3.) *. 4.
|
||||
|
||||
(* Calculate rotation from [p] to [p'] about [c] with the shorter arc-length.
|
||||
When arc-lengths are equal, the result may be either. *)
|
||||
let rotation (cx, cy) (px, py) (px', py') =
|
||||
let px = px -. cx and py = py -. cy
|
||||
and px' = px' -. cx and py' = py' -. cy in
|
||||
let a = px *. py' -. py *. px'
|
||||
and b = px *. px' +. py *. py' in
|
||||
atan2 a b
|
||||
|
||||
(* The absolute angle to a point [p] from a centre [c]. The angle is the
|
||||
rotation clockwise (i.e the first quadrant encountered has positive [x] and [y]
|
||||
values) from East. When the point is [(0, 0)], the result is [0].*)
|
||||
let angle_to (cx, cy) (px, py) =
|
||||
let r = atan2 (py -. cy) (px -. cx) in
|
||||
if r < 0. then r +. 2. *. pi else r
|
||||
|
||||
(* Restrict an angle [a] to one of those at $s, 2s, 3s\ldots$. We find the two
|
||||
candidate angles, and see which [a] is numerically closer to. The candidate
|
||||
points are taken modulo $2\pi$ for this to work. *)
|
||||
let restrict_angle s a =
|
||||
let p = mod_float (floor (a /. s) *. s) (2. *. pi) in
|
||||
let p' = mod_float (p +. s) (2. *. pi) in
|
||||
if abs_float (p -. a) < abs_float (p' -. a) then p else p'
|
||||
|
||||
(* \section{Some Useful Shapes} *)
|
||||
|
||||
(* Make a quarter-circle from a single bezier curve from [s] to $(s + \pi / 2)
|
||||
\bmod 2\pi$ with centre [c] and radius [r]. We cheat by making the standard
|
||||
quarter from [(1, 0)] to [(0, 1)] and rotating using the [Transform] module.
|
||||
*)
|
||||
let quarter s (cx, cy) r =
|
||||
let standard_quarter_points =
|
||||
[(1., 0.); (1., kappa); (kappa, 1.); (0., 1.)]
|
||||
and transform =
|
||||
[Pdftransform.Translate(cx, cy);
|
||||
Pdftransform.Scale((0., 0.), r, r);
|
||||
Pdftransform.Rotate((0., 0.), s)]
|
||||
in
|
||||
match
|
||||
map (Pdftransform.transform transform) standard_quarter_points
|
||||
with
|
||||
| [p; q; r; s] -> Cpdfgraphics.Bezier(p, q, r, s)
|
||||
| _ -> raise (Pdf.PDFError ("Shapes.quarter: inconsistency"))
|
||||
|
||||
(* The anticlockwise variant. *)
|
||||
let quarter_anticlockwise s c r =
|
||||
match quarter s c r with
|
||||
| Cpdfgraphics.Bezier(p, q, r, s) -> Cpdfgraphics.Bezier(s, r, q, p)
|
||||
| _ -> raise (Pdf.PDFError "Shapes.quarter_anticlockwise: inconsistency")
|
||||
|
||||
(* Some of the following functions generate what is supposed to be a connected
|
||||
list of segments. However, since they operate by calculating each segment
|
||||
seperately, floating point inaccuracies can arise, making the end of one
|
||||
segment misalign with the start of the next. This function corrects the defect
|
||||
by copying the end of one segment to the beginning of the next. We only need to
|
||||
deal with bezier segments for now. *)
|
||||
let rec joinsegs segments =
|
||||
match segments with
|
||||
| [] -> []
|
||||
| [x] -> [x]
|
||||
| Cpdfgraphics.Bezier(_, _, _, d) as s::Cpdfgraphics.Bezier(_, b', c', d')::rest ->
|
||||
s::joinsegs (Cpdfgraphics.Bezier(d, b', c', d')::rest)
|
||||
| _ -> raise (Pdf.PDFError "PDFShapes.joinsegs: Segment not supported")
|
||||
|
||||
(* This version sets the start and end points to p1 and p2 respectively. Used
|
||||
for ensuring round joins join correctly to the rails they connect *)
|
||||
let joinsegs_ends p1 p2 segments =
|
||||
match joinsegs segments with
|
||||
| [] -> []
|
||||
| [Cpdfgraphics.Bezier(a, b, c, d)] -> [Cpdfgraphics.Bezier(p1, b, c, p2)]
|
||||
| segs ->
|
||||
match extremes_and_middle segs with
|
||||
| Cpdfgraphics.Bezier(_, b, c, d), m, Cpdfgraphics.Bezier(a', b', c', _) ->
|
||||
Cpdfgraphics.Bezier(p1, b, c, d)::m @ [Cpdfgraphics.Bezier(a', b', c', p2)]
|
||||
| _ -> raise (Pdf.PDFError "PDFShapes.joinsegs_ends: Segment not supported")
|
||||
|
||||
(* The shorter arc made from bezier curves from [p1] to [p2] with centre [c].
|
||||
The arc is formed from zero or more quarter arcs rotated accordingly, and at
|
||||
most one partial arc produced by truncating a quarter arc, again rotated. If
|
||||
[p1=p2], no segments are produced. If the two curves defined by the arguments
|
||||
are of equal length, the one chosen is undefined. *)
|
||||
(*i let arc p1 p2 c =
|
||||
let ninety = pi /. 2.
|
||||
and angletogo = rotation c p1 p2 (*r signed angle to turn through *)
|
||||
and abs_angle = angle_to c p1 (*r absolute angle to the first point *)
|
||||
and r = distance_between p1 c in (*r radius of the resultant arc *)
|
||||
let quarter, ninety_abs =
|
||||
if angletogo > 0.
|
||||
then quarter, ninety
|
||||
else quarter_anticlockwise, ~-.ninety
|
||||
in
|
||||
let segments = ref []
|
||||
and angletogo = ref (abs_float angletogo) (*r Have dealt with sign. *)
|
||||
and abs_angle = ref abs_angle in
|
||||
while !angletogo > 0. do
|
||||
if !angletogo >= ninety then
|
||||
begin
|
||||
angletogo := !angletogo -. ninety;
|
||||
segments := (quarter !abs_angle c r)::!segments;
|
||||
abs_angle := mod_float (!abs_angle +. ninety_abs) (2. *. pi)
|
||||
end
|
||||
else
|
||||
(* Calculate a partial arc to finish, if required. *)
|
||||
if !angletogo > 0. then
|
||||
begin
|
||||
let q = quarter !abs_angle c r in
|
||||
let portion_needed = !angletogo /. ninety in
|
||||
let portion, _ = Polygon.bezier_split portion_needed q in
|
||||
segments := portion::!segments;
|
||||
angletogo := 0.
|
||||
end;
|
||||
done;
|
||||
joinsegs_ends p1 p2 (rev !segments) i*)
|
||||
|
||||
(* Approximate a circle using four bezier curves.*)
|
||||
let circle x y r =
|
||||
Cpdfgraphics.NonZero,
|
||||
[(Cpdfgraphics.Not_hole,
|
||||
Cpdfgraphics.Closed,
|
||||
joinsegs
|
||||
[quarter 0. (x, y) r;
|
||||
quarter (pi /. 2.) (x, y) r;
|
||||
quarter pi (x, y) r;
|
||||
quarter (3. *. pi /. 2.) (x, y) r ])]
|
||||
|
||||
let rectangle x y w h =
|
||||
(Cpdfgraphics.EvenOdd,
|
||||
([(Cpdfgraphics.Not_hole,
|
||||
Cpdfgraphics.Closed,
|
||||
[Cpdfgraphics.Straight ((x, y), (x +. w, y));
|
||||
Cpdfgraphics.Straight ((x +. w, y), (x +. w, y +. h));
|
||||
Cpdfgraphics.Straight ((x +. w, y +. h), (x, y +. h));
|
||||
Cpdfgraphics.Straight ((x, y +. h), (x, y))])]))
|
17
cpdfshape.mli
Normal file
17
cpdfshape.mli
Normal file
@ -0,0 +1,17 @@
|
||||
(** Basic Shapes *)
|
||||
|
||||
(** The factor by which the radius of a circle is multiplied to find the length
|
||||
of the bezier control lines when approximating quarter arcs to make circles. *)
|
||||
val kappa : float
|
||||
|
||||
(** Calling [restrict_angle s a] restricts an angle [a] to one of those at [s,
|
||||
2s, 3s...] returning the chosen one. *)
|
||||
val restrict_angle : float -> float -> float
|
||||
|
||||
(** Calling [circle x y r] builds a path representing a circle at [(x, y)] with
|
||||
radius [r]. *)
|
||||
val circle : float -> float -> float -> Cpdfgraphics.path
|
||||
|
||||
(** Calling [rectangle x y w h] builds a path representing a rectangle with top
|
||||
left [(x, y)], width [w] and height [h]. *)
|
||||
val rectangle : float -> float -> float -> float -> Cpdfgraphics.path
|
Loading…
x
Reference in New Issue
Block a user